Skip to content

Validation

Broken links are easy to miss – pages get renamed or moved, and references silently stop working. Zensical validates all internal links at build time by scanning every Markdown file and resolving all references: inline links, reference-style links, footnotes, link definitions, and anchors.

Additionally, the build can be aborted when issues are found by enabling strict mode.

Configuration

Validation is enabled by default. All checks can be individually toggled:

[project.validation]
unresolved_references = true
unresolved_footnotes = true
unused_definitions = true
unused_footnotes = true
shadowed_definitions = true
shadowed_footnotes = true
invalid_links = true
invalid_link_anchors = true
validation:
  unresolved_references: true
  unresolved_footnotes: true
  unused_definitions: true
  unused_footnotes: true
  shadowed_definitions: true
  shadowed_footnotes: true
  invalid_links: true
  invalid_link_anchors: true

Validation can also be completely disabled:

[project]
validation = false
validation: false

The following checks for links and footnotes are available:


unresolved_references

Warn when a link or image reference has no matching definition.

[project.validation]
unresolved_references = true
validation:
  unresolved_references: true

Example

index.md
This is an [unresolved reference][id].
$ zensical build
...
Warning: unresolved link reference
   ╭─[ index.md:1:35 ]

 1 │ This is an [unresolved reference][id].
   │                                   ─┬
   │                                    ╰── unresolved link reference
───╯

unresolved_footnotes

Warn when a footnote reference has no matching definition.

[project.validation]
unresolved_footnotes = true
validation:
  unresolved_footnotes: true

Example

index.md
This is an unresolved footnote[^id].
$ zensical build
...
Warning: unresolved footnote reference
   ╭─[ index.md:1:33 ]

 1 │ This is an unresolved footnote[^id].
   │                                 ─┬
   │                                  ╰── unresolved footnote reference
───╯

unused_definitions

Warn when a link definition is never referenced.

[project.validation]
unused_definitions = true
validation:
  unused_definitions: true

Example

index.md
[id]: https://example.com
$ zensical build
...
Warning: unused link definition
   ╭─[ index.md:1:2 ]

 1 │ [id]: https://example.com
   │  ─┬
   │   ╰── unused link definition
───╯

unused_footnotes

Warn when a footnote definition is never referenced.

[project.validation]
unused_footnotes = true
validation:
  unused_footnotes: true

Example

index.md
[^id]: This footnote is never referenced.
$ zensical build
...
Warning: unused footnote definition
   ╭─[ index.md:1:3 ]

 1 │ [^id]: This footnote is never referenced.
   │   ─┬
   │    ╰── unused footnote definition
───╯

shadowed_definitions

Warn when a link definition is declared more than once.

[project.validation]
shadowed_definitions = true
validation:
  shadowed_definitions: true

Example

index.md
This [reference][id] has two definitions.

[id]: https://example.com/shadowed
[id]: https://example.com
$ zensical build
...
Warning: shadowed link definition
   ╭─[ index.md:3:2 ]

 3 │ [id]: https://example.com/shadowed
   │  ─┬
   │   ╰── shadowed link definition
───╯

shadowed_footnotes

Warn when a footnote definition is declared more than once.

[project.validation]
shadowed_footnotes = true
validation:
  shadowed_footnotes: true

Example

index.md
This footnote[^id] has two definitions.

[^id]: 1st definition
[^id]: 2nd definition
$ zensical build
...
Warning: shadowed footnote definition
   ╭─[ index.md:3:3 ]

 3 │ [^id]: 1st definition
   │   ─┬
   │    ╰── shadowed footnote definition
───╯

Warn when a link points to a page that does not exist.

[project.validation]
invalid_links = true
validation:
  invalid_links: true

Example

index.md
Oh no, [this page] does not exit.

[this page]: non-existent.md
$ zensical build
...
Warning: page does not exist
   ╭─[ index.md:3:14 ]

 3 │ [this page]: non-existent.md
   │              ───────┬───────
   │                     ╰───────── page does not exist
───╯

Warn when a link points to an anchor that does not exist.

[project.validation]
invalid_link_anchors = true
validation:
  invalid_link_anchors: true

Example

index.md
Oh no, [this section] does not exit.

[this section]: page.md#non-existent
$ zensical build
...
Warning: anchor does not exist
   ╭─[ index.md:3:31 ]

 3 │ [this section]: page.md#non-existent
   │                         ──────┬─────
   │                               ╰─────── anchor does not exist
───╯

Usage

Just you write your content as usual, and Zensical will automatically validate all links and footnotes at build time.

If issues are found, a warning is emitted with the file name, line number, and a helpful message.

Escaping

If you want to intentionally wrap a phrase with brackets without creating a link, you can escape the opening bracket with a backslash, i.e., \[. The closing bracket does not need to be escaped:

This is not a \[link](https://example.com).

While escaping is technically not necessary when no link definition is present, it is recommended to avoid accidentally creating a link when a definition is later added.

Moreover, link validation assumes that bracketed phrases are intended to be links or footnotes, so it will emit warnings for any unresolved references or unused definitions. Escaping allows you to avoid these warnings and clearly indicate that the brackets are not meant to create a link.

Strict mode

If you want to enforce link validation and fail the build when issues are found, you can enable strict mode by using the --strict command-line option:

index.md
This is an [unresolved reference][id].
$ zensical build --strict
...
Warning: unresolved link reference
   ╭─[ index.md:1:35 ]

 1 │ This is an [unresolved reference][id].
   │                                   ─┬
   │                                    ╰── unresolved link reference
───╯
1 issue found
Aborted because --strict flag is set

The build is aborted after reporting all issues, and the exit code is set to 1 to indicate failure. This can be useful in CI/CD pipelines to ensure that all links are valid before deploying the site.