Build Failures: Linux Server Docs Batch (2026-03-01)
This report documents all build failures encountered after a batch rewrite of the linux-server documentation module and the exact fixes applied to resolve each one.
1) Incident Summary
- Date: 2026-03-01
- Trigger: Batch rewrite of
docs/server/linux-server/documentation (archiving, backup, webserver, automation modules). - Detection method: Running
build-if-changed.shand watching logs. - Total distinct errors fixed: 6 categories.
- Final result: Build succeeded —
[SUCCESS] Generated static files in "build".
2) Error Catalogue
Error A — Inverted YAML Frontmatter (content inside --- block)
Files affected:
08-archiving-retrieval/xz.mdx08-archiving-retrieval/zstd.mdx
Symptom:
YAMLException: end of the stream or a document separator is expected
Can't process doc metadata for doc at path=/app/docs/server/linux-server/08-archiving-retrieval/xz.mdx
Root cause:
During an AI-assisted rewrite, new content sections (## Restore checklist, ## Advanced tuning) were accidentally pasted inside the opening --- frontmatter block instead of after the closing ---. The real frontmatter fields (id:, title:, etc.) ended up mid-document, resulting in a frontmatter block that was valid-looking but contained raw Markdown and fence blocks.
Broken pattern:
---
## Restore checklist
Use this checklist...
- [ ] Test integrity (`xz -t ...`).
```bash title="restore-drill.sh"
...
...
id: xz-compression title: xz description: ... sidebar_label: xz sidebar_position: 14
**Fix applied:**
1. Replaced the malformed opening block with a proper 7-line frontmatter block.
2. Appended the displaced content sections (`## Restore checklist`, `## Advanced tuning`) at the bottom of the file where they belong.
**Correct pattern:**
```yaml
---
id: xz-compression
title: xz
description: Compress single files with xz...
sidebar_label: xz
sidebar_position: 14
---
Error B — Unquoted Colon in YAML Frontmatter Values
Files affected:
10-backup-disaster-recovery/disaster-recovery-workflow.mdx(colon indescription)10-backup-disaster-recovery/plugin-wpbackup-limitation.mdx(colon intitleanddescription)11-automation-task-execution/cron.mdx(colon indescription)
Symptom:
YAMLException: incomplete explicit mapping pair; a key node is missed;
or followed by a non-tabulated empty line at line N, column N
Root cause:
YAML treats : followed by a space as a key-value separator. When a frontmatter field value contains a colon (e.g. in a "Title: Subtitle" pattern or a "steps: identify, rebuild…" description), the YAML parser interprets everything after the colon as a new mapping key.
Broken pattern:
title: WordPress Backup Plugins: Limitations and Best Use
description: Restore a WordPress site after an incident using a safe sequence: identify restore point, rebuild...
Fix applied: Wrap any frontmatter value containing a colon in double quotes.
title: "WordPress Backup Plugins: Limitations and Best Use"
description: "Restore a WordPress site after an incident using a safe sequence: identify restore point, rebuild..."
Quick detection scan:
grep -rn "^\(title\|description\|sidebar_label\):.*:.*" \
/opt/docker-data/apps/docusaurus/site/docs/ \
--include="*.mdx" | grep -v ': "' | grep -v ": '"
Any line returned that is in a frontmatter block (lines 1–10 of the file) needs its value quoted.
Error C — MDX JSX Closing Tag After Markdown List Item
Files affected:
07-webserver-php-ssl/editing-phpini.mdx
Symptom:
Error: MDX compilation failed for file "/app/docs/.../editing-phpini.mdx"
Cause: Expected the closing tag `</TabItem>` either after the end of
`listItem` (63:52) or another opening tag after the start of `listItem` (60:1)
Root cause:
Same class of issue as a previous incident (see troubleshooting-mdx-listitem-tabitem-build-failure.mdx). A <TabItem> contained markdown bullet-list items (- item), and the </TabItem> closing tag appeared at the same indentation level with a 2-space leading indent. The MDX parser treats indented JSX tags as a continuation of the list-item context, which is invalid.
Broken pattern:
<TabItem value="phpfpm" label="Nginx/Apache + PHP-FPM" default>
Common locations:
- `/etc/php/<version>/fpm/php.ini` (Debian/Ubuntu)
- `/etc/php.ini` + `/etc/php.d/` (RHEL family)
</TabItem> ← 2-space indent causes parser to treat this as list continuation
Fix applied: Remove leading indentation from all </TabItem> and <TabItem> tags that follow markdown list content. Place them at column 0.
<TabItem value="phpfpm" label="Nginx/Apache + PHP-FPM" default>
Common locations:
- `/etc/php/<version>/fpm/php.ini` (Debian/Ubuntu)
- `/etc/php.ini` + `/etc/php.d/` (RHEL family)
</TabItem> ← column 0, no leading spaces
Rule: <TabItem> and </TabItem> tags must always be at column 0 (no indentation) when the tab content contains markdown block elements (lists, blockquotes, headings).
Error D — Bare <digit Parsed as JSX Tag
Files affected:
07-webserver-php-ssl/open-litespeed.mdx
Symptom:
Error: MDX compilation failed for file "/app/docs/.../open-litespeed.mdx"
Cause: Unexpected character `5` (U+0035) before name, expected a character
that can start a name, such as a letter, `$`, or `_`
Root cause:
The file contained <50 concurrent in a bullet list. In MDX, any < not inside a fenced code block is treated as the start of a JSX tag. <50 is not a valid JSX tag name (tag names must start with a letter, $, or _), so compilation fails.
Broken pattern (in markdown prose):
- Small blog (2 vCPU / 4GB, <50 concurrent) -> Low impact
Fix applied: HTML-escape < as < when used as a "less than" symbol in MDX prose outside code fences.
- Small blog (2 vCPU / 4GB, <50 concurrent) -> Low impact
Quick detection scan for raw <digit outside code fences:
grep -n "<[0-9]" \
/opt/docker-data/apps/docusaurus/site/docs/server/linux-server/07-webserver-php-ssl/open-litespeed.mdx
Error E — Missing Tabs/TabItem Import in MDX File
Files affected:
07-webserver-php-ssl/certbot--lets-encripts-ssl.mdx
Symptom (static site generation stage, not compile stage):
Error: Cannot find module or Unexpected token
Error: Can't render static file for pathname "/docs/server/linux-server/webserver-php-ssl/certbot-lets-encrypt-ssl"
Cause: Expected component `TabItem` to be defined: you likely forgot to import...
Root cause:
The file used <Tabs> and <TabItem> JSX components throughout, but was missing the required import statements at the top of the file. Docusaurus does not auto-import these — every .mdx file that uses them must declare the imports explicitly.
Broken pattern (no import at top):
---
id: certbot-lets-encrypt-ssl
...
---
# Certbot and Let's Encrypt SSL
<Tabs>
<TabItem value="debian" label="Debian/Ubuntu">
...
Fix applied: Add imports immediately after the closing --- frontmatter delimiter.
---
id: certbot-lets-encrypt-ssl
...
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
# Certbot and Let's Encrypt SSL
Rule: Every .mdx file that uses <Tabs>, <TabItem>, <Badge>, or any other @theme/ component must import it explicitly. There is no global auto-import in Docusaurus.
Error F — Missing Prism Language in additionalLanguages Config
Issue type: Not a build failure — a rendering gap (no syntax highlighting for bash, nginx, yaml, etc.).
Symptom: Code blocks tagged ```bash, ```nginx, ```ini, ```yaml, etc. rendered as plain text without any token coloring on the live site.
Root cause:
Docusaurus ships Prism with only a minimal default language set (JavaScript, CSS, Markup/HTML). All other languages must be explicitly declared in docusaurus.config.js inside prism.additionalLanguages. Without this, the language grammar is not bundled and no highlighting is applied.
Original config (no additionalLanguages):
prism: {
theme: lightTheme,
darkTheme: darkTheme,
},
Fix applied — add all languages used across the docs:
prism: {
theme: lightTheme,
darkTheme: darkTheme,
additionalLanguages: [
'bash',
'shell-session',
'ini',
'yaml',
'nginx',
'apacheconf',
'sql',
'php',
'python',
'diff',
'docker',
'json',
'markup', // covers XML/HTML/SVG (NOT 'xml' — that name does not exist in Prism)
'regex',
'powershell',
'log',
],
},
Do not add 'xml' — that name does not exist as a standalone Prism component. Use 'markup' instead, which covers HTML, XML, and SVG.
To verify what language names are valid before adding them:
sudo docker exec docusaurus ls /app/node_modules/prismjs/components/ \
| sed 's/prism-//;s/\.js$//' | grep -v '\.min' | sort
3) Build Command
All fixes were validated by running:
bash /home/rezriz/github/01-production/docusaurus-build-if-changed/build-if-changed.sh
Successful output ends with:
[SUCCESS] Generated static files in "build".
[build-if-changed] Build validation passed
[build-if-changed] Restarting container 'docusaurus'
[build-if-changed] Done.
4) Prevention Checklist
| Rule | Check |
|---|---|
Frontmatter always starts on line 1 with --- | No content before the opening --- |
| Frontmatter values with colons are quoted | Wrap in "..." if value contains : |
</TabItem> stays at column 0 after list content | No leading spaces on JSX closing tags |
< in prose text is escaped | Use < for less-than symbols in MDX prose |
Every <Tabs> file has its imports | import Tabs from '@theme/Tabs' present |
| New languages added to Prism config | Verify name exists in prismjs/components/ before adding |
5) Related Troubleshooting Docs
- YAML Syntax & Broken Links — general YAML frontmatter colon rule
- Google Ads Docs YAML Frontmatter 502 Fix — previous colon-in-title incident
- MDX List Items Inside TabItem Build Failure — previous
</TabItem>+ list conflict - Undefined Components (MDX) — general missing import reference