{"record":{"id":"c11e6fbe23138b55","repo":"getzola/zola","slug":"relative-link-not-found","errorCode":null,"errorMessage":"Relative link {} not found.","messagePattern":"Relative link (.+?) not found\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"components/utils/src/site.rs","lineNumber":33,"sourceCode":"    pub anchor: Option<String>,\n}\n\n/// Resolves an internal link (of the `@/posts/something.md#hey` sort) to its absolute link and\n/// returns the path + anchor as well\npub fn resolve_internal_link(\n    link: &str,\n    permalinks: &HashMap<String, String>,\n) -> Result<ResolvedInternalLink> {\n    // First we remove the @/ since that's zola specific\n    let clean_link = link.replacen(\"@/\", \"\", 1);\n    // Then we remove any potential anchor\n    // parts[0] will be the file path and parts[1] the anchor if present\n    let parts = clean_link.split('#').collect::<Vec<_>>();\n    // If we have slugification turned off, we might end up with some escaped characters so we need\n    // to decode them first\n    let decoded = percent_decode(parts[0].as_bytes()).decode_utf8_lossy().to_string();\n    let target =\n        permalinks.get(&decoded).ok_or_else(|| anyhow!(\"Relative link {} not found.\", link))?;\n    if parts.len() > 1 {\n        Ok(ResolvedInternalLink {\n            permalink: format!(\"{}#{}\", target, parts[1]),\n            md_path: decoded,\n            anchor: Some(parts[1].to_string()),\n        })\n    } else {\n        Ok(ResolvedInternalLink { permalink: target.to_string(), md_path: decoded, anchor: None })\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::collections::HashMap;\n\n    use super::resolve_internal_link;\n\n    #[test]","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/getzola/zola/blob/61d30828217957120309db657950224edf9707e2/components/utils/src/site.rs#L15-L51","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before (content/posts/a.md)\n[other post](./oher-post.md)\n// after\n[other post](./other-post.md)","handlingStrategy":"validation","validationCode":"// Verify a link target exists before writing it:\n// test -f content/posts/other-post.md && echo OK\n// or run: zola check  (reports every unresolvable internal link)","typeGuard":null,"tryCatchPattern":"// Rust (library consumers, e.g. helpers calling fix_link)\nmatch resolve_internal_link(link, &permalinks, config_path) {\n    Ok(resolved) => resolved.permalink,\n    Err(_) => {\n        log::warn!(\"dead internal link dropped: {link}\");\n        String::new()\n    }\n}","preventionTips":["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"],"tags":["rust","links","permalinks"],"backgroundTag":"broken-internal-link","analyzedSha":"61d30828217957120309db657950224edf9707e2","analyzedAt":"2026-09-03T14:39:09.727Z","contentChangedAt":"2026-09-03T14:39:09.727Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}