Antora Xref Troubleshooting Guide
The Problem
Antora builds fail with errors like:
target of xref not found: 01/index.adoc
target of xref not found: infra-ops::runbooks/backup-strategy.adoc
target of xref not found: template.adoc
These errors occur because Antora xrefs do NOT work like file system paths.
Root Cause Analysis
Issue 1: Cross-Component Xrefs in Spoke Repos
Symptom:
target of xref not found: infra-ops::runbooks/backup-strategy.adoc
Root Cause:
Cross-component xrefs (xref:component::path.adoc[]) require ALL components to be present during build. They ONLY resolve when building through the aggregator (domus-docs).
When building a spoke repo standalone (e.g., cd domus-captures && make), the aggregator isn’t involved. Antora only sees ONE component, so references to other components fail.
Why This Happens:
# Aggregator build (works)
domus-docs/
├── antora-playbook-local.yml # Lists ALL components
└── builds with: npx antora antora-playbook-local.yml
→ All 12 components loaded
→ xref:infra-ops::... resolves ✓
# Standalone build (fails)
domus-captures/
├── antora-playbook-local.yml # Lists ONLY captures
└── builds with: npx antora antora-playbook-local.yml
→ Only captures component loaded
→ xref:infra-ops::... has nothing to resolve to ✗
Issue 2: Relative Path Xrefs
Symptom:
target of xref not found: ../06/index.adoc
target of xref not found: template.adoc
Root Cause:
Antora xref paths are relative to the pages/ directory, NOT to the current file’s location.
| What You Write | What You Expect | What Antora Does |
|---|---|---|
|
Resolve relative to current file |
Looks for |
|
Same directory as current file |
Looks for |
|
- |
Looks for |
Why This Happens:
Antora creates a virtual file system from all pages. The xref macro resolves paths from the module’s pages/ root, not from the current file’s directory.
# File: pages/2025/05/index.adoc
# Trying to link to: pages/2025/06/index.adoc
# WRONG - file-system thinking
xref:../06/index.adoc[] # Antora sees: pages/../06/index.adoc ✗
# CORRECT - Antora path from pages/ root
xref:2025/06/index.adoc[] # Antora sees: pages/2025/06/index.adoc ✓
Issue 3: Same-Directory Xrefs
Symptom:
# From pages/runbooks/index.adoc
target of xref not found: template.adoc
Root Cause:
Even for files in the same directory, you need the full path from pages/:
# WRONG
xref:template.adoc[] # Looks for pages/template.adoc ✗
# CORRECT
xref:runbooks/template.adoc[] # Looks for pages/runbooks/template.adoc ✓
Prevention Strategy
Rule 1: Never Use Cross-Component Xrefs in Spoke Repos
| Location | Allowed Xrefs |
|---|---|
Spoke repos (domus-captures, domus-infra-ops, etc.) |
Only same-component xrefs + plain text references |
Hub repo (domus-docs/docs/) |
Cross-component xrefs allowed |
Instead of:
* xref:infra-ops::runbooks/backup-strategy.adoc[Backup Strategy]
Write:
* Backup Strategy (infra-ops)
Rule 2: Always Use Absolute Paths from pages/
Instead of:
xref:../06/index.adoc[June]
xref:template.adoc[Template]
Write:
xref:2025/06/index.adoc[June]
xref:runbooks/template.adoc[Template]
Quick Reference
Fixing Existing Errors
Architecture Summary
domus-docs (AGGREGATOR/HUB)
├── Can use cross-component xrefs ✓
├── Builds ALL components together
├── docs/modules/ROOT/pages/ ← Cross-component xrefs OK here
└── BUT: index.adoc should have ZERO cross-component deps!
(Spoke playbooks pull in hub but not all components)
domus-* (SPOKE REPOS)
├── Cannot use cross-component xrefs ✗
├── Build standalone OR through aggregator
├── Must use plain text for cross-component references
└── docs/.../modules/ROOT/pages/ ← Only same-component xrefs
| The hub landing page (index.adoc) is special. Spoke playbooks (like domus-infra-ops) include domus-docs as a source. If the hub’s index.adoc has cross-component xrefs (e.g., to captures), they will fail during spoke builds because captures isn’t in that playbook. Keep index.adoc free of cross-component dependencies. |
Lessons Learned (2026-02-13 Incident)
What Happened
-
Created 2025 historical documentation with month subdirectories
-
Used intuitive file-system relative paths (
../06/index.adoc) -
Used cross-component xrefs to link to infra-ops, linux-ops, etc.
-
Builds failed with 30+ xref errors
-
Spent significant time debugging because the files existed
Why It Was Confusing
-
Files existed on disk -
lsshowed them, git tracked them -
Paths looked correct -
../06/index.adocmakes sense for file systems -
Worked conceptually - the mental model was "link to this other file"
The Mental Model Shift
| File System Thinking | Antora Thinking |
|---|---|
Paths are relative to current file |
Paths are relative to |
|
|
Same directory = just filename |
Same directory = full path from pages/ |
All files are accessible |
Only components in playbook are visible |
The Fix Process
-
Identify:
make 2>&1 \| grep error -
Categorize: Cross-component vs relative path vs same-directory
-
Fix cross-component: Convert to plain text
"Label (component)" -
Fix relative paths: Use absolute paths from
pages/ -
Verify: Rebuild and confirm zero errors
-
Document: Update CLAUDE.md with prevention rules
Addendum: 2026-02-19 Incident
New Error Types Discovered
Issue 4: Cross-Component Images
Symptom:
target of image not found: home::diagrams/work-tracker-2026-02-compact.svg
Root Cause:
Unlike xrefs, images CANNOT use cross-component syntax (component::path). The image:: macro only supports:
-
Local paths (relative to module’s
images/directory) -
Absolute URLs (…;)
| Syntax | Works? |
|---|---|
|
Yes (local) |
|
Yes (local subdirectory) |
|
Yes (URL) |
|
NO (cross-component not supported) |
|
NO (cross-component not supported) |
Fix:
Cross-component images have NO workaround. Options:
-
Copy the image to the local component’s
images/directory -
Use URL if image is hosted (e.g.,
image::https://docs.example.com/images/file.svg[]) -
Remove the reference and link to the page instead (if in hub only):
// WRONG - never works image::home::diagrams/work-tracker.svg[] // OPTION - external URL (if hosted) image::https://docs.domusdigitalis.dev/home/2026/_images/diagrams/work-tracker.svg[] // OPTION - remove entirely and use plain text See xref:captures::2026/02/WRKLOG-2026-02-19.adoc[Work Tracker] for current status.
Cross-component xrefs in domus-docs ALSO fail when building from spoke playbooks. If you run make from domus-infra-ops, its playbook pulls in domus-docs but NOT all other components (like captures). The hub’s cross-component xrefs then fail. Solution: The hub landing page should have ZERO cross-component dependencies. Only use cross-component xrefs in hub pages that are never built through spoke playbooks.
|
Issue 5: Same-Directory Xrefs in Nested Paths
Symptom:
# File: pages/cli/ise/ers/dacls.adoc
target of xref not found: authz-profiles.adoc
target of xref not found: network-devices.adoc
Root Cause:
Files exist in the same directory (cli/ise/ers/), but Antora resolves ALL xrefs from the module root (pages/), not from the current file’s location.
# Current file: pages/cli/ise/ers/dacls.adoc
# Target file: pages/cli/ise/ers/authz-profiles.adoc
# WRONG - assumes same-directory resolution
xref:authz-profiles.adoc[] # Looks for pages/authz-profiles.adoc ✗
# CORRECT - full path from module root
xref:cli/ise/ers/authz-profiles.adoc[] # Looks for pages/cli/ise/ers/authz-profiles.adoc ✓
Rule: ALWAYS use full paths from pages/, regardless of current file location.
Issue 6: List Continuation with Code Blocks
Symptom:
list item index: expected 1, got 3
Root Cause:
Numbered lists with code blocks between items break list continuation:
// WRONG - list breaks after code block
1. First step
2. Second step:
[source,bash]
some command
3. Third step <-- Antora sees this as "1" not "3"
Fix:
Use AsciiDoc auto-numbering (.) with continuation marker (+):
// CORRECT - proper list continuation
. First step
. Second step:
+
[source,bash]
----
some command
----
. Third step <-- Correctly continues as item 3
Updated Prevention Checklist
Before committing documentation changes:
-
Run
grep -rn 'xref:[a-z-]*::' docs/- empty in spoke repos -
Run
grep -rn 'xref:\.\.' docs/- empty in all repos -
Run
grep -rn 'image::[a-z-]*::' docs/- empty in all repos (NEW) -
Check numbered lists with code blocks use
.and+continuation -
Run
makeand verify zero errors/warnings
Pre-Commit Grep Commands
# Cross-component xrefs (forbidden in spoke repos)
grep -rn 'xref:[a-z-]*::' docs/
# Relative path xrefs (always forbidden)
grep -rn 'xref:\.\.' docs/
# Cross-component images (always forbidden)
grep -rn 'image::[a-z-]*::' docs/
# Hardcoded list numbers that might break
grep -rn '^[0-9]\+\. ' docs/