getzola/zola · error

Found {} broken internal anchor link(s) {}

Error message

Found {} broken internal anchor link(s)
{}

What it means

During load in check mode, Zola validates every internal link's #anchor against the target page's heading anchors. When internal_level is set to Error and any internal anchor links are broken (the anchor doesn't exist on the target page), load returns this error listing each bad link.

Source

Thrown at components/site/src/lib.rs:369

        // Needs to be done after rendering markdown as we only get the anchors at that point
        let internal_link_messages = link_checking::check_internal_links_with_anchors(self);

        // log any broken internal links and error out if needed
        if !internal_link_messages.is_empty() {
            let messages: Vec<String> = internal_link_messages
                .iter()
                .enumerate()
                .map(|(i, msg)| format!("  {}. {}", i + 1, msg))
                .collect();
            let msg = format!(
                "Found {} broken internal anchor link(s)\n{}",
                messages.len(),
                messages.join("\n")
            );
            match self.config.link_checker.internal_level {
                config::LinkCheckerLevel::Warn => log::warn!("{msg}"),
                config::LinkCheckerLevel::Error => return Err(anyhow!(msg)),
            }
        }

        // check external links, log the results, and error out if needed
        if self.config.is_in_check_mode() && self.check_external_links {
            let external_link_messages = link_checking::check_external_links(self);
            if !external_link_messages.is_empty() {
                let messages: Vec<String> = external_link_messages
                    .iter()
                    .enumerate()
                    .map(|(i, msg)| format!("  {}. {}", i + 1, msg))
                    .collect();
                let msg = format!(
                    "Found {} broken external link(s)\n{}",
                    messages.len(),
                    messages.join("\n")
                );
                match self.config.link_checker.external_level {

View on GitHub (pinned to 61d3082821)

Solutions

  1. Fix or remove each listed broken anchor link in your markdown
  2. Re-run zola check until zero broken anchors are reported
  3. Temporarily set link_checker.internal_level = "warn" in config.toml if you only want warnings instead of a failing build
  4. Use exact heading text — anchors are slugified from headings, so renamed headings change the anchor

Example fix

// before (content.md)
[details](@/guide.md#configuration)
// after (heading in guide.md is "## Configuring the site")
[details](@/guide.md#configuring-the-site)
Defensive patterns

Strategy: validation

Validate before calling

// Run zola check in CI before building:
// zola check && zola build
// Or keep anchors consistent: link only to headings whose slug you verify

Try / catch

// Treat broken-anchor reports as warnings by configuring:
// [link_checker]
// internal_level = "warn"
// then inspect warnings in build output

Prevention

When it happens

Trigger: Calling zola check (or Site::load with check mode) where a markdown link like [x](@/page.md#missing-anchor) or [](...#anchor) references an anchor that does not exist in the rendered target page; internal_level = "error" in config link_checker.

Common situations: Renaming or deleting a heading in a target page that other pages link to with a #fragment; editing a heading without updating slugified anchors; typos in anchor names.

Related errors


AI-assisted analysis of getzola/zola@61d3082821 (2026-09-03). Data as JSON: /api/errors/35f7e32ddb099e8a. Report an issue: GitHub.