Skip to main content

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.sh and 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.mdx
  • 08-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"
...
warning

...

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 in description)
  • 10-backup-disaster-recovery/plugin-wpbackup-limitation.mdx (colon in title and description)
  • 11-automation-task-execution/cron.mdx (colon in description)

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 &lt; when used as a "less than" symbol in MDX prose outside code fences.

- Small blog (2 vCPU / 4GB, &lt;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',
],
},
warning

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

RuleCheck
Frontmatter always starts on line 1 with ---No content before the opening ---
Frontmatter values with colons are quotedWrap in "..." if value contains :
</TabItem> stays at column 0 after list contentNo leading spaces on JSX closing tags
< in prose text is escapedUse &lt; for less-than symbols in MDX prose
Every <Tabs> file has its importsimport Tabs from '@theme/Tabs' present
New languages added to Prism configVerify name exists in prismjs/components/ before adding