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

发布时间:2026/9/14 19:04:21

【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/9/10 18:38:16

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

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

2026/9/13 1:57:40

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

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

2026/9/14 19:00:20

vscode settings.json 配置冲突?用 TaoToken 让 Codex 逐项核

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

2026/9/14 19:00:20

前端转全栈别乱学:15 个 Node.js 高质量资源,按能力地图整理

前端转全栈别乱学:15 个 Node.js 高质量资源,按能力地图整理前端转全栈,最容易踩的坑不是资源不够。而是学习顺序错了。 也许有小伙伴说ai写代码还有必要看这个地图吗? 我的回答有必要,ai虽然可以写代码,但…

2026/9/14 19:00:20

制造业ERP与MES实施顺序决策及系统协同指南

摘要:制造业数字化转型中,ERP与MES的建设顺序直接影响项目周期、实施成本与协同效果。本文从两者的核心定位差异出发,分析不同企业场景下的实施顺序决策逻辑,给出可量化的决策框架、系统协同架构设计、数据流与接口规范&#xff0…

2026/9/14 19:00:20

从零跑通智能自动照明:ESPHome 光照传感器实战指南

从零跑通智能自动照明:ESPHome 光照传感器实战指南 【免费下载链接】esphome ESPHome is a system to control your ESP32, ESP8266, BK72xx, RP2040 by simple yet powerful configuration files and control them remotely through Home Automation systems. 项…

2026/9/14 18:55:19

水质监测管理平台:水质实时监测・化验记录全链路业务建模

前言水质监测管理,是守护供水安全的最后一道防线,覆盖在线水质数据自动采集、实时监测、国标限值比对、超标分级预警、异常处置复核,以及实验室采样、化验、审核、归档全流程,业务对标国家标准、时效要求高、处置复核需双人把关、…

2026/9/14 2:17:50

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/14 0:03:22

KCF目标跟踪算法与OTB工程实现:毕业设计实战解析

简介:这是一份基于KCF核相关滤波算法、融合尺度池与抗遮挡处理的目标检测跟踪MATLAB完整源码,主要面向计算机相关专业准备毕业设计、课程设计或期末大作业的学生,也适合需要项目实战练习的初学者。源码在OTB数据集上完成验证,能够…

2026/9/14 0:03:22

语音情感识别实战:Keras实现LSTM、CNN、SVM与MLP多模型对比

简介:面向语音情感识别入门与进阶开发者,这份基于Keras的项目源码完整实现了LSTM、CNN、SVM、MLP四种模型,兼容Python3.8与Keras/TensorFlow2环境。压缩包内含49个文件,大小约70.31MB,主体包括Python脚本、yaml/json配…

2026/9/14 11:59:31

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/14 13:53:59

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/14 11:22:57

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

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

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

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