getzola/zola · error

Relative link {} not found.

Error message

Relative link {} not found.

What it means

resolve_internal_link looks up a cleaned, percent-decoded internal link path in the site's permalinks map (built from all known pages/sections/assets). If the path is not found — meaning no content file corresponds to the link — this error is thrown. It is the root cause of 'broken internal link' failures across the library.

Source

Thrown at components/utils/src/site.rs:33

    pub anchor: Option<String>,
}

/// Resolves an internal link (of the `@/posts/something.md#hey` sort) to its absolute link and
/// returns the path + anchor as well
pub fn resolve_internal_link(
    link: &str,
    permalinks: &HashMap<String, String>,
) -> Result<ResolvedInternalLink> {
    // First we remove the @/ since that's zola specific
    let clean_link = link.replacen("@/", "", 1);
    // Then we remove any potential anchor
    // parts[0] will be the file path and parts[1] the anchor if present
    let parts = clean_link.split('#').collect::<Vec<_>>();
    // If we have slugification turned off, we might end up with some escaped characters so we need
    // to decode them first
    let decoded = percent_decode(parts[0].as_bytes()).decode_utf8_lossy().to_string();
    let target =
        permalinks.get(&decoded).ok_or_else(|| anyhow!("Relative link {} not found.", link))?;
    if parts.len() > 1 {
        Ok(ResolvedInternalLink {
            permalink: format!("{}#{}", target, parts[1]),
            md_path: decoded,
            anchor: Some(parts[1].to_string()),
        })
    } else {
        Ok(ResolvedInternalLink { permalink: target.to_string(), md_path: decoded, anchor: None })
    }
}

#[cfg(test)]
mod tests {
    use std::collections::HashMap;

    use super::resolve_internal_link;

    #[test]

View on GitHub (pinned to 61d3082821)

Solutions

  1. Fix the link path in the source markdown to point at an existing file (check spelling, case-sensitivity, and extension)
  2. Create the missing target file/asset the link references
  3. Run zola check to list all unresolvable links and fix each one
  4. If the file exists but isn't linked, verify it's inside the content/ or the page's colocated asset directory so it registers a permalink

Example fix

// before (content/posts/a.md)
[other post](./oher-post.md)
// after
[other post](./other-post.md)
Defensive patterns

Strategy: validation

Validate before calling

// Verify a link target exists before writing it:
// test -f content/posts/other-post.md && echo OK
// or run: zola check  (reports every unresolvable internal link)

Try / catch

// Rust (library consumers, e.g. helpers calling fix_link)
match resolve_internal_link(link, &permalinks, config_path) {
    Ok(resolved) => resolved.permalink,
    Err(_) => {
        log::warn!("dead internal link dropped: {link}");
        String::new()
    }
}

Prevention

When it happens

Trigger: Any internal link (@/ page/section reference, markdown relative link, colocated asset reference) whose target path does not exist in the permalinks map: missing file, wrong relative path, mistyped filename/extension, or incorrect percent-encoding.

Common situations: Renaming or deleting a page that other pages link to; linking before creating the target content file; typos in relative paths like ./ ../ or @/ prefixes; linking to assets not actually colocated with the page.

Related errors


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