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
- Fix the link path in the source markdown to point at an existing file (check spelling, case-sensitivity, and extension)
- Create the missing target file/asset the link references
- Run zola check to list all unresolvable links and fix each one
- 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
- Run `zola check` in CI to catch dead internal links
- Prefer @/ internal links over fragile relative paths where supported
- Grep for links to a file before renaming or deleting it
- Watch for case-sensitivity: file paths must match exactly
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
- could not parse domain `{}` from link
- could not parse domain `{}` from link: `{}`
- Failed to convert bytes to string : {}
- SASS path conflict: "{}" and "{}" both compile to "{}"
- {:?} is not inside the base site directory {:?}
AI-assisted analysis of getzola/zola@61d3082821 (2026-09-03).
Data as JSON: /api/errors/c11e6fbe23138b55.
Report an issue: GitHub.