Antora Troubleshooting
Overview
This runbook documents common Antora build issues encountered across the domus-* documentation repositories and their solutions.
Attribute Reference Warnings
Problem
Antora/AsciiDoctor interprets text within curly braces {…} as attribute references. When an attribute doesn’t exist, you get warnings like:
[WARN] (asciidoctor): skipping reference to missing attribute: n
file: .../vim-neovim-reference.adoc
Common Patterns That Trigger Warnings
| Pattern | Context | Example |
|---|---|---|
|
Vim commands (repeat count) |
|
|
Vim text objects |
|
|
Shell variables in code blocks |
|
|
Documentation placeholders |
|
|
API URL path parameters |
|
Solution: Escape Curly Braces
Escape curly braces with backslash to prevent AsciiDoc interpretation:
| Before (triggers warning) | After (escaped) |
|---|---|
|
|
|
|
|
|
|
|
|
The backslash goes before each curly brace, not before the dollar sign:
|
Finding Attribute Warnings
Run the Antora build and grep for warnings:
make 2>&1 | grep -E "WARN.*missing attribute"
Or for detailed output:
npx antora antora-playbook-local.yml 2>&1 | grep -A2 "WARN"
Fixing Attribute Warnings
-
Identify the file and attribute name from the warning
-
Search for unescaped occurrences:
grep -n '{attributename}' path/to/file.adoc -
Edit the file to escape curly braces:
# Single occurrence sed -i 's/{n}/\\{n\\}/g' file.adoc # Or use your editor -
Rebuild and verify:
make 2>&1 | grep WARN
Code Block Delimiter Issues
Problem
AsciiDoc uses ---- for listing blocks. If your code content contains ---- (e.g., PowerShell table output), the parser gets confused:
ERROR: level 0 sections can only be used when doctype is book
ERROR: unterminated listing block
Cross-Reference (xref) Errors
Cross-reference errors are the most common build issues. This section covers all scenarios.
Error Format
[ERROR] (asciidoctor): target of xref not found: component::path/to/file.adoc
file: /path/to/source-file.adoc
source: /path/to/repo (branch: main)
Issue 1: Single vs Double Colon
Symptom: target of xref not found: domus-infra-ops:runbooks/file.adoc
Cause: Antora xrefs require double colons (::) between component and page.
Fix:
| Wrong | Correct |
|---|---|
`xref:domus-infra-ops:runbooks/file.adoc[`] |
`xref:infra-ops::runbooks/file.adoc[`] |
`xref:component:page.adoc[`] |
`xref:component::page.adoc[`] |
Issue 2: Wrong Component Name
Symptom: target of xref not found: domus-ise-linux:page.adoc
Cause: The component name comes from antora.yml, not the repo name.
Fix: Look up the actual component name:
# Find component names
grep "^name:" ~/atelier/_bibliotheca/domus-*/docs/asciidoc/antora.yml
Component Name Reference:
| Repository | Component Name (use in xref) |
|---|---|
domus-docs |
|
domus-infra-ops |
|
domus-ise-linux |
|
domus-ise-ops |
|
domus-netapi-docs |
|
domus-secrets-ops |
|
domus-linux-ops |
|
domus-identity-ops |
|
domus-python |
|
domus-automation-ops |
|
Issue 3: Component Not in Playbook
Symptom: target of xref not found: identity-ops::index.adoc (but component exists)
Cause: The referenced component isn’t included in the playbook’s content.sources.
Fix: Add the missing component to the playbook:
content:
sources:
# ... existing sources ...
- url: /home/evanusmodestus/atelier/_bibliotheca/domus-identity-ops
start_path: docs/asciidoc
branches: HEAD
Check what’s in a playbook:
grep -A1 "url:" docs/asciidoc/antora-playbook.yml
Issue 4: Page Doesn’t Exist
Symptom: target of xref not found: infra-ops::services/keycloak.adoc
Cause: The target page doesn’t exist in the component.
Fix:
-
Check if the page exists:
ls ~/atelier/_bibliotheca/domus-infra-ops/docs/asciidoc/modules/ROOT/pages/services/ -
If not, point to an existing page (usually the component index):
# Instead of non-existent page xref:infra-ops::services/keycloak.adoc[Keycloak] # Point to component root xref:infra-ops::index.adoc[Infrastructure Operations]
Issue 5: Relative Paths in Subdirectories
Symptom: target of xref not found: hardened-dacl.adoc (file exists in same directory)
Cause: Antora xrefs are always relative to pages/, not the current file’s directory.
Fix: Use the full path from pages/:
| File Location | Wrong xref | Correct xref |
|---|---|---|
|
`xref:hardened-dacl.adoc[`] |
`xref:03-ise-config/hardened-dacl.adoc[`] |
|
`xref:dot1x-switches.adoc[`] |
`xref:network/dot1x-switches.adoc[`] |
|
`xref:disaster-recovery.adoc[`] |
`xref:runbooks/disaster-recovery.adoc[`] |
Issue 6: Duplicate Component in Playbook
Symptom:
FATAL (antora): Duplicate nav in 2026@prj-ise-home-linux: modules/ROOT/nav.adoc
1: .../domus-ise-linux/docs/asciidoc/modules/ROOT/nav.adoc
2: .../domus-ise-linux/docs/asciidoc/modules/ROOT/nav.adoc
Cause: The same repository URL appears twice in the playbook’s content.sources.
Fix: Remove the duplicate entry from the playbook:
# Find duplicates in playbook
grep "url:" docs/asciidoc/antora-playbook.yml | sort | uniq -d
Issue 7: Spoke Playbook Missing Common Components
Symptom: Build works in hub (domus-docs) but fails in spoke repos with xref errors.
Cause: Spoke playbooks don’t include all components referenced by domus-docs roadmaps.
Commonly Missing Components:
| Component | Required For |
|---|---|
|
Identity federation roadmap, Keycloak xrefs |
|
ISE operations xrefs, zero-trust roadmap |
|
Network automation roadmap, Python tooling xrefs |
Fix: Add all three to every spoke playbook:
- url: /home/evanusmodestus/atelier/_bibliotheca/domus-identity-ops
start_path: docs/asciidoc
branches: HEAD
- url: /home/evanusmodestus/atelier/_bibliotheca/domus-ise-ops
start_path: docs/asciidoc
branches: HEAD
- url: /home/evanusmodestus/atelier/_bibliotheca/domus-python
start_path: docs/asciidoc
branches: HEAD
Issue 8: Spoke Playbook Missing Hub and All Cross-References
Symptom: Multiple xref errors for various components when building a spoke repo.
Cause: Some spoke playbooks were created for truly standalone builds (only their own component) and never updated to include the hub or cross-referenced components.
Diagnostic:
# Check if playbook only lists itself
grep -c "url:" docs/asciidoc/antora-playbook.yml
# If result is 1, playbook needs all components added
Fix: Add the complete component set:
content:
sources:
# This project
- url: /home/evanusmodestus/atelier/_bibliotheca/domus-YOUR-REPO
start_path: docs/asciidoc
branches: HEAD
# Domus hub
- url: /home/evanusmodestus/atelier/_bibliotheca/domus-docs
start_path: docs
branches: HEAD
# Cross-referenced components
- url: /home/evanusmodestus/atelier/_bibliotheca/domus-infra-ops
start_path: docs/asciidoc
branches: HEAD
- url: /home/evanusmodestus/atelier/_bibliotheca/domus-ise-linux
start_path: docs/asciidoc
branches: HEAD
- url: /home/evanusmodestus/atelier/_bibliotheca/domus-netapi-docs
start_path: docs/asciidoc
branches: HEAD
- url: /home/evanusmodestus/atelier/_bibliotheca/domus-secrets-ops
start_path: docs/asciidoc
branches: HEAD
- url: /home/evanusmodestus/atelier/_bibliotheca/domus-identity-ops
start_path: docs/asciidoc
branches: HEAD
- url: /home/evanusmodestus/atelier/_bibliotheca/domus-ise-ops
start_path: docs/asciidoc
branches: HEAD
- url: /home/evanusmodestus/atelier/_bibliotheca/domus-python
start_path: docs/asciidoc
branches: HEAD
Batch Fix Commands
Find all broken xrefs in build output:
make 2>&1 | grep -E "target of xref not found" | sort -u
Find xrefs with single colon (wrong syntax):
grep -rn 'xref:domus-[a-z-]*:' docs/
grep -rn 'xref:[a-z-]*:[^:]' docs/ # Single colon not followed by colon
Find xrefs in a specific repo:
grep -rn "xref:" ~/atelier/_bibliotheca/domus-docs/docs/modules/ROOT/pages/
Adding Components to All Spoke Playbooks
When adding a new component, update playbooks in all spoke repos:
# List all spoke repo playbooks
ls ~/atelier/_bibliotheca/domus-*/docs/asciidoc/antora-playbook.yml
# Add to each (example for domus-identity-ops)
cat >> ~/atelier/_bibliotheca/domus-ise-linux/docs/asciidoc/antora-playbook.yml << 'EOF'
- url: /home/evanusmodestus/atelier/_bibliotheca/domus-identity-ops
start_path: docs/asciidoc
branches: HEAD
EOF
Prevention: xref Syntax Reference
// Same component, same module xref:other-page.adoc[Link Text] // Same component, subdirectory xref:subdir/page.adoc[Link Text] // Different component xref:component::page.adoc[Link Text] // Different component, subdirectory xref:component::subdir/page.adoc[Link Text] // Different component, different module xref:component:module:page.adoc[Link Text]
Documenting xref Examples
When writing documentation that shows xref syntax examples, use one of these methods to prevent interpretation:
-
Literal blocks (
….) - Best for multi-line examples -
Passthrough (
) - Best for inline examples in tables -
Backticks inside pass -
\`xref:...[\]`
|
|
Cloudflare Access Blocking Static Assets
Problem
When an Antora site is protected by Cloudflare Access (Zero Trust), the page may appear white or unstyled even though the HTML loads correctly.
Symptoms:
-
Page loads but has no CSS (white background, unstyled text)
-
Browser console shows 302 redirects for
//css/site.css,//js/site.js -
document.documentElement.getAttribute('data-theme')returns the correct value but styles don’t apply -
Preview deployments work, but custom domain doesn’t
Cause
Cloudflare Access protects the entire domain, including static assets at /_/*. When the browser requests CSS/JS files, Access redirects them to the login page instead of serving the files.
Diagnostic:
curl -sI "https://docs.domusdigitalis.dev/_/css/site.css" | head -5
# If you see HTTP/2 302 with location: cloudflareaccess.com -> Access is blocking
# Should be HTTP/2 200 for CSS to load
Solution
Create a separate Access application for the static assets path with a bypass policy:
Option 1: Using netapi (recommended)
# One command creates the app and bypass policy
netapi cloudflare access bypass-static docs.domusdigitalis.dev
Option 2: Using curl
# Step 1: Create Access app for static path
curl -X POST "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/access/apps" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"name": "domus-docs-static",
"type": "self_hosted",
"domain": "docs.domusdigitalis.dev/_/*",
"session_duration": "24h"
}'
# Step 2: Add bypass policy (use app ID from step 1)
curl -X POST "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/access/apps/<APP_ID>/policies" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"name": "Bypass Static Assets",
"decision": "bypass",
"include": [{"everyone": {}}]
}'
Verify fix:
curl -sI "https://docs.domusdigitalis.dev/_/css/site.css" | head -1
# Should show: HTTP/2 200
Clear browser cache (Ctrl+Shift+R) after applying the fix.
Security Note
The /_/* path only contains static assets (CSS, JS, fonts, images). These are the same assets in the public Antora UI bundle - not sensitive documentation content.
For detailed steps, see: Cloudflare Access + Antora Runbook
Reference
Repositories in Aggregation
| Repository | Component | Content |
|---|---|---|
domus-docs |
|
Aggregator, hub landing page, roadmaps |
domus-infra-ops |
|
Infrastructure runbooks, PKI, services |
domus-linux-ops |
|
Linux commands, KVM, editors |
domus-netapi-docs |
|
netapi CLI documentation |
domus-ise-linux |
|
802.1X EAP-TLS for Linux |
domus-ise-ops |
|
ISE operations, policies, integration |
domus-ise-windows |
|
802.1X EAP-TLS for Windows |
domus-secrets-ops |
|
Secrets management (dsec, gopass) |
domus-identity-ops |
|
Keycloak, SAML, identity federation |
domus-python |
|
Python CLI tools, automation |
domus-automation-ops |
|
Jinja2 templates, GitOps patterns |
domus-captures |
|
Worklogs, session notes |
domus-siem-ops |
|
Wazuh, QRadar, Sentinel, threat detection |
domus-o11y-ops |
|
Prometheus, Grafana, Loki observability |
domus-windows-ops |
|
PowerShell, WSL, Windows certificates |