REF-001 Broken links
Overview
Section titled “Overview”Validates that relative links inside Markdown (links of the form [text](./file.md)) point to files that actually exist. If the target does not exist, an error is reported. Anchor fragments (#section) are excluded from the file existence check — their validity is the responsibility of REF-005 Anchors.
Why it matters
Section titled “Why it matters”Renaming, deleting, or moving files is the most common way Markdown documents decay over time. AI-generated content can also introduce links to files that never existed. Broken links pass markdownlint and CI silently, so detecting them mechanically is essential.
Options
Section titled “Options”| Field | Type | Required | Description |
|---|---|---|---|
exclude | string[] | — | Array of glob patterns whose link targets are excluded from validation |
siteRouter | object | — | Resolves routed URLs (e.g. /docs/x/) emitted by SSGs to real files |
The rule works even with no options at all. Use exclude for cases where you intentionally link to documents or generated artifacts outside the include set.
siteRouter — resolving SSG routed URLs
Section titled “siteRouter — resolving SSG routed URLs”By default REF-001 resolves relative links (./file.md) as file system paths. SSGs like Astro Starlight emit routed URLs (/docs/x/ or /ja/docs/x/) that don’t correspond to literal file paths, so without siteRouter they are flagged as broken.
| Sub-field | Type | Required | Description |
|---|---|---|---|
preset | string | — | Known SSG preset. Currently "starlight" only |
contentDir | string | ✓ | Content directory (e.g. "packages/site/src/content/docs") |
defaultLocale | string | — | Default locale identifier. Use "root" for Starlight’s prefix-less root locale |
locales | string[] | — | Supported locale identifiers (e.g. ["root", "ja", "ko", "zh"]) |
urlPrefix | string | — | (Generic, no preset) URL prefix to strip before resolving |
indexFile | string | — | Index file name to try first (default: "index.md") |
Starlight (i18n) example:
{ "rule": "ref001", "options": { "siteRouter": { "preset": "starlight", "contentDir": "packages/site/src/content/docs", "defaultLocale": "root", "locales": ["root", "ja", "ko", "zh"] } }}/docs/get-started/ resolves to <contentDir>/docs/get-started/index.md or <contentDir>/docs/get-started.md. /ja/docs/get-started/ resolves to <contentDir>/ja/docs/get-started/index.md.
Generic (no preset) example:
{ "rule": "ref001", "options": { "siteRouter": { "contentDir": "src/pages", "urlPrefix": "/wiki", "indexFile": "README.md" } }}/wiki/some-page/ resolves to src/pages/some-page/README.md.
Bad example
Section titled “Bad example”See [architecture](./architecture.md) for details.If ./architecture.md does not exist, this triggers a violation.
docs/overview.md line 1 error Link target "./architecture.md" does not exist REF-001After fix
Section titled “After fix”See [architecture](./architecture.md) for details.Either create the target file architecture.md or update the link to the correct path.
Configuration example
Section titled “Configuration example”{ "rule": "ref001", "options": { "exclude": ["generated/**/*.md"] }}Related rules
Section titled “Related rules”- REF-005 Anchors — Validates that anchors (
#section) inside the linked file exist - REF-006 Image references — Validates image references (
) instead of links - STR-001 Required files — Validates file existence at the project level rather than per-link