【Bug已解决】CI is failing for pages build and deployment: Liquid syntax error: ‘if‘ tag was never closed

发布时间:2026/11/5 12:53:15

【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/11/4 14:00:42

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

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

2026/11/4 8:21:01

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

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

2026/11/5 12:29:32

Text to SQL系统第三层实现:复杂查询意图识别与优化

1. 项目概述Text to SQL系统作为自然语言处理与数据库技术交叉领域的重要应用,正在深刻改变着非技术人员与数据库交互的方式。这个看似简单的"输入问题,输出SQL"功能背后,隐藏着复杂的实现逻辑和技术挑战。今天我们就来拆解这个系统…

2026/11/5 12:29:32

Text-to-SQL系统架构解析与性能优化实践

1. 文本到SQL转换系统的核心价值 在数据驱动的时代,SQL作为与数据库交互的标准语言,其重要性不言而喻。但编写SQL查询需要专业技能,这成为了许多非技术用户获取数据的障碍。Text-to-SQL系统正是为解决这一痛点而生,它允许用户用自…

2026/11/5 12:29:32

钉钉办公自动化:OpenClaw Skill原理与应用

1. 项目概述 OpenClaw Skill是一个基于自动化原理设计的办公效率工具,专门针对钉钉平台的操作优化。它能模拟人工操作流程,实现消息自动收发、审批流程处理、考勤数据管理等高频办公场景的自动化。我在实际使用中发现,这个工具特别适合需要频…

2026/11/5 12:29:32

AI编程辅助工具:5个提升代码可用性的Prompt模板

1. 项目背景与核心价值 去年团队引入AI编程辅助工具时,我们经历了从"玩具demo"到"生产级应用"的艰难跨越。最深的体会是:需求沟通的质量直接决定了AI产出的可用性。那些模糊的"做个用户管理系统"之类的需求,AI…

2026/11/5 12:24:32

云原生智能体协同架构在城市轨道交通中的应用

1. 新一代城市轨道交通云原生智能体协同架构概述城市轨道交通作为现代都市的"大动脉",其运营效率和服务质量直接影响着千万市民的日常生活。近年来,随着云计算、人工智能等技术的快速发展,传统的轨道交通智能化系统架构正面临着前所…

2026/11/5 12:14:29

Stimulsoft Reports 2019.2.2 落地指南:老系统兼容与版本陷阱规避

简介:Stimulsoft Reports 2019.2.2 是一款面向.NET开发者的专业报表控件,适用于WinForms、ASP.NET及WPF平台,帮助开发者快速构建数据可视化报表与打印输出功能,尤其适合中高级C#工程师在企业级管理系统、BI看板或财务报表模块中集…

2026/11/4 12:38:23

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/4 21:04:25

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/5 0:02:07

多智能体协作系统架构设计与实践优化

1. 多智能体协作系统的时代背景2017年Transformer架构的诞生彻底改变了人工智能的发展轨迹。当行业还沉浸在单一大模型带来的性能突破时,Anthropic的研究团队已经将目光投向了更前沿的领域——如何让多个智能体通过协作产生超越单体能力的系统智能。这就像交响乐团中…

2026/11/5 0:02:07

大模型系统性入门实战指南:从原理到部署微调的全链路路线

提到大模型的系统性入门,我先说个结论:这事真不难,难的是资料太散、路线太多,今天这个框架明天那个工具,新人很容易在“收藏夹吃灰”和“教程看到一半放弃”之间反复横跳。市面上关于大模型的内容少说有几十万篇&#…

2026/11/5 0:02:07

鸿蒙文件系统沙箱机制解析与开发实践

1. 鸿蒙文件系统沙箱机制深度解析作为一名从Android转向鸿蒙开发的工程师,我深刻体会到文件系统访问是迁移过程中最先遇到的"拦路虎"。在Android上运行良好的文件读写代码,在鸿蒙真机环境却频频出现文件写入后神秘消失或直接抛出权限拒绝错误的…

2026/11/4 12:38:23

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/4 21:04:25

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑