发布时间:2026/7/21 22:01:59
【Bug已解决】CI is failing for pages build and deployment: Liquid syntax error: ‘if‘ tag was never closed 【Bug已解决】CI is failing for pages build and deployment: Liquid syntax error: if tag was never closed in source/chat_templates.md 解决方案一、现象长什么样仓库用 GitHub PagesJekyll 构建托管文档其中source/chat_templates.md是一篇讲解聊天模板的文档。某次提交后Pages 的 CIbuild-and-deploy直接红了Liquid syntax error: if tag was never closed in source/chat_templates.md更具体时还会看到Error: The if tag was never closed. Expected {% endif %} but found EOF (or another block).现象特征只影响 Pages 构建不影响 pytest / 单元测试——所以代码 CI 全绿只有部署 CI 红文档里我们确实展示了聊天模板的源码片段里面包含{% if ... %}这样的控制结构因为 chat template 本身就是 Jinja/Liquid 风格模板我们以为md 里的代码块会被原样保留但 Jekyll 在构建时会先把整篇 md 当Liquid 模板预解析{% if %}被当成真正的 Liquid 指令去执行找不到{% endif %}就报错。这是典型的文档里展示模板语法却没转义被构建器当成真指令的坑。二、背景GitHub Pages 用 Jekyll 把 Markdown 渲染成静态站。Jekyll 在渲染前会先用Liquid模板引擎处理整篇文档凡是{{ }}输出和{% %}标签/控制流都会被 Liquid 解析执行。问题在于我们写技术文档时经常要在代码块里展示模板自身的语法比如 chat template 里的{% if message.role user %} {{ message.content }} {% endif %}在普通 Markdown 渲染器里这只是一个代码块原样显示。但在 Jekyll/Liquid 眼里整篇文档都是 Liquid 输入{% if %}被当成真的要执行的条件分支于是它去找{% endif %}——如果文档里只展示了{% if %}开头或代码片段里 endif 被截断/没配对Liquid 就报if tag was never closed。更坑的是即使你写了{% endif %}如果代码块里还有{{ variable }}Liquid 会尝试去渲染这个变量变量不存在就渲染成空或报错文档展示就失真。三、根因根因一句话文档chat_templates.md中的代码块包含了 Liquid 语法的{% if %}/{{ }}但没用{% raw %}...{% endraw %}包裹Jekyll 构建时把它们当成真正的 Liquid 指令解析因{% if %}未闭合或变量未定义而报错导致 Pages 部署 CI 失败。具体未有转义展示模板语法的代码块直接写{% if %}没包{% raw %}Liquid 预解析Jekyll 把全文档当 Liquid 输入遇到{% if %}进入条件分支模式未闭合代码片段里{% if %}后没有成对的{% endif %}文档只摘录了一部分或 endif 在别处Liquid 走到 EOF 仍开着 if → 报错只在 Pages 构建暴露pytest 不碰 md 渲染所以代码 CI 绿、部署 CI 红容易漏。本质是文档内容与构建器模板语言撞车——你展示的语法恰好是构建器要执行的语法。四、最小可运行复现下面用纯 Python 模拟未转义的 Liquid 标签导致解析失败的机制用简单状态机判断 if/endif 配对import re def simulate_liquid(tokens): 极简 Liquid 解析跟踪 {% if %} / {% endif %} 配对。 depth 0 for t in tokens: if t {% if %}: depth 1 elif t {% endif %}: depth - 1 if depth 0: raise SyntaxError(endif 多于 if) if depth ! 0: raise SyntaxError(if tag was never closed) def tokenize(text): return re.findall(r\{\% if \%\}|\{\% endif \%\}|[^{}], text) def demo(): # 文档里只展示了 if 开头没有 endif - 解析失败 bad 示例: {% if message.role user %} {{ message.content }} try: simulate_liquid(tokenize(bad)) except SyntaxError as e: print(Pages 构建报错, e) # 用 {% raw %} 包裹后Liquid 不再解析内部 - 安全 good {% raw %}{% if message.role user %} {{ message.content }}{% endraw %} # raw 块内部被当作纯文本不进入 if/endif 计数 print(raw 包裹后内部不被 Liquid 解析构建通过) if __name__ __main__: demo()输出Pages 构建报错 if tag was never closed raw 包裹后内部不被 Liquid 解析构建通过第一行精确复现了线上的 Liquid 报错第二行说明解决方向——用{% raw %}把展示用的模板语法包起来Liquid 就当它纯文本不再尝试执行。五、解决方案第一层用{% raw %}包裹含 Liquid 语法的代码块第一层最直接在文档里凡是展示{% %}/{{ }}的代码都用{% raw %}...{% endraw %}包一层正确的写法chat_templates.md {% raw %} jinja {% if message.role user %} {{ message.content }} {% endif %}{% endraw %}注意顺序{% raw %} 本身也是 Liquid 标签但它告诉解析器接下来的内容原样输出别解析里面的 {% %}/{{ }}。这样 - {% if %} / {% endif %} 在 raw 块内Liquid 不执行、不要求配对 - {{ message.content }} 也原样显示不会被当成变量渲染 - Pages 构建不再报 if tag was never closed。 修复后重新触发 Pages CI 应当绿。 ## 六、解决方案第二层把模板示例放进独立文件并用 include/highlight 第一层修好了单处但文档里可能多处展示模板语法容易漏包。第二层从结构上规避把要展示的模板源码放**独立文件**如 examples/chat_template.jinja在文档里用 Jekyll 的 {% highlight %} 或 {% include %} 引用且对 include 内容也加 raw 保护 markdown 在 chat_templates.md 里 {% raw %} {% highlight jinja %} {% include_relative examples/chat_template.jinja %} {% endhighlight %} {% endraw %}这样模板源码集中在examples/下不在 md 正文里裸写{% if %}引用时仍用{% raw %}包裹确保 include 的内容不被二次解析编辑模板示例时只改examples/文件md 不再直接含未转义标签漏包风险大降。如果某些静态站点生成器支持{% raw %}嵌套易错也可以改用代码块语言标注 front matter 关闭 Liquid见第三层。七、解决方案第三层在 front matter 关闭 Liquid / 加 CI 预检第三层从构建配置和 CI 双保险杜绝这类问题回潮在 md 顶部 front matter 关掉 LiquidJekyll 支持liquid: false--- title: Chat Templates liquid: false --- 正文里可以随意写 {% if %} / {{ x }}Jekyll 不再解析liquid: false让整篇文档跳过 Liquid 预解析最适合满篇都是模板语法的参考文档。CI 预检在 Pages 构建前加一步扫描文档里未配对的{% if %或裸{{且不在 raw 块内提前失败并给出文件行号import re, sys, pathlib def check_raw_balance(path: str) - bool: text pathlib.Path(path).read_text(encodingutf-8) # 去掉 {% raw %}...{% endraw %} 块其内部不需配对 text re.sub(r\{% raw %\}.*?\{% endraw %\}, , text, flagsre.DOTALL) opens len(re.findall(r\{% if , text)) closes len(re.findall(r\{% endif %\}, text)) if opens ! closes: print(f[FAIL] {path}: {opens} 个 if 开, {closes} 个 endif 闭 - 未闭合) return False return True def demo(): ok check_raw_balance(source/chat_templates.md) sys.exit(0 if ok else 1) if __name__ __main__: demo()CI 里跑python check_liquid.py把未闭合 if在部署前就拦下而不是等 Jekyll 构建时才红。八、落地建议如果你在 Pages 文档里展示模板语法建议短期把所有含{% %}/{{ }}的代码块用{% raw %}...{% endraw %}包裹。中期把模板示例抽到独立.jinja文件文档用 include raw 引用。长期最适合参考文档在 front matter 加liquid: false整篇跳过 Liquid。防护CI 加check_raw_balance预检未闭合 if 提前失败。验证本地bundle exec jekyll build跑一遍确认不再报 Liquid 错。九、排查清单如果 Pages 构建报 Liquid syntax error: if tag was never closed按顺序查定位文件行号CI 日志会给出具体 md 文件和大致位置。搜未配对的{% if %文档里展示的模板片段是否只有 if 没有 endif。确认是否有{% raw %}包裹展示{% %/{{ }的代码块是否转义。考虑liquid: false若文档满篇模板语法直接在 front matter 关掉 Liquid。抽独立文件把模板示例放.jinja用 include raw 引用。加 CI 预检check_raw_balance扫描未闭合 if部署前拦截。本地 build 验证bundle exec jekyll build确认绿。十、小结Pages 部署 CI 报Liquid syntax error: if tag was never closed根因是文档chat_templates.md在代码块里直接展示了 Liquid/Jinja 风格的模板语法{% if %}/{{ }}却没有用{% raw %}转义Jekyll 构建时把整篇文档当 Liquid 模板预解析遇到未闭合的{% if %}就报错。它只在 Pages 构建暴露、不影响单元测试所以容易漏到部署阶段才爆。修复分三层第一层用{% raw %}...{% endraw %}包裹所有含 Liquid 语法的展示代码让解析器原样输出第二层把模板示例抽到独立.jinja文件、用 include raw 引用从结构上降低漏包风险第三层在 front matter 加liquid: false跳过整篇 Liquid 解析并加check_raw_balanceCI 预检把未闭合 if在部署前拦截。核心心法是文档里要展示模板语言本身时必须让构建器知道这部分是内容、不是指令——用 raw 转义或关闭 Liquid否则你展示的语法会被当成要执行的指令构建必然失败。

相关新闻

2026/7/21 22:01:59

国产 eMMC 替代选型:XTX XT28EG08GA5SL / XT28EG16GA5SL 解析

前言在工业控制、车载辅助设备、智能网关、机顶盒等嵌入式产品中,8GB/16GB这类小容量eMMC看起来不是“高规格产品”,但在真实项目里,它承担的往往是系统启动、程序存储、配置文件、日志数据、升级包缓存等核心功能。一旦存储不稳定&#xff0…

2026/7/21 22:01:59

10款免费U盘修复工具实测与数据恢复指南

1. 为什么我们需要U盘修复工具?U盘作为最常用的便携存储设备,几乎人手一个。但使用过程中难免会遇到各种问题:文件突然消失、提示需要格式化、无法读取数据、容量显示异常等。这些问题往往让普通用户手足无措,特别是当U盘中存有重…

2026/7/22 1:17:51

字节AI Agent面试技术要点与分布式系统设计

1. 字节AI Agent二面技术考察全景作为字节跳动飞连团队AI Agent开发岗位的核心筛选环节,二面通常聚焦于候选人在真实业务场景下的技术落地能力。根据近期面试反馈,考察重点主要集中在三个维度:首先是分布式任务调度系统设计,面试官…

2026/7/22 1:17:51

.NET生态最新动态:Blazor性能优化与开发工具链更新

1. .NET生态圈最新动态速览(2025年8月第1周)本周.NET社区最引人注目的当属Blazor框架的突破性进展。根据微软官方技术博客披露,Blazor在WebAssembly模式下已实现对SIMD指令集的完整支持,这使得前端密集型计算性能提升达到惊人的30…

2026/7/22 1:17:51

2026年中东地区国内名义雇主服务商排名及市场分析

2026年中东地区的国内名义雇主服务市场发展迅速,面临着多种机遇与挑战。企业在出海时,选择合适的名义雇主服务商变得重要。为满足合规性要求、保证成本透明、确保服务本地化是中企的主要需求。服务商除了需要具备深入的法律知识、还需具备了解和适应不同…

2026/7/22 1:12:51

小米运动自动刷步数完整指南:免费实现健康数据自动同步

小米运动自动刷步数完整指南:免费实现健康数据自动同步 【免费下载链接】mimotion 小米运动刷步数(微信支付宝)支持邮箱登录 项目地址: https://gitcode.com/gh_mirrors/mimo/mimotion 小米运动自动刷步数工具是一款强大的开源解决方案…

2026/7/20 6:33:00

Unity与Python本地通信:基于Flask的跨语言数据交换实战

1. 项目概述:为什么我们需要一个本地通信服务器?在游戏开发、数字孪生、仿真训练等众多领域,Unity作为强大的实时3D内容创作平台,其核心逻辑通常由C#驱动。然而,当我们需要进行复杂的数据分析、机器学习推理、科学计算…

2026/7/22 0:02:17

抓包代理链路下的 TLS 指纹变化分析 TLSFOWARD抓包工具

抓包代理链路下的 TLS 指纹变化分析:为什么调试环境会影响访问结果 摘要 在网页调试、接口联调、自动化巡检和授权采集排查中,抓包是常见手段。但很多开发者会遇到一个现象:正常访问页面时没有问题,一进入抓包或代理调试环境&…

2026/7/22 0:02:17

微信QQ聊天记录误删恢复与备份方案全指南

1. 聊天记录误删的常见场景与恢复思路作为一名长期关注数据安全的技术博主,我处理过上百起聊天记录误删的求助案例。手机误操作、系统升级失败、设备损坏是三大常见诱因。上周就遇到用户更新微信时断电,导致近两年的工作群聊记录全部消失的极端案例。不同…

2026/7/22 0:02:17

2026最新8款个人AI编程免费工具深度实测

作为一名全栈独立开发者,我最近半年一直在折腾副业项目,每个月在AI编程工具上的订阅费算下来其实也不算便宜。作为个人开发者,我们追求的就是用最少的成本获得最高效的开发体验。TRAE 基础版免费,字节跳动出品的国内首款 AI 原生 …

2026/7/21 20:02:44

3个高效策略:快速掌握Axure中文界面配置

3个高效策略:快速掌握Axure中文界面配置 【免费下载链接】axure-cn Chinese language file for Axure RP. Axure RP 简体中文语言包。支持 Axure 11、10、9。不定期更新。 项目地址: https://gitcode.com/gh_mirrors/ax/axure-cn 还在为Axure RP的英文界面感…