{"record":{"id":"2166d0716e3bf2f6","repo":"rust-lang/mdBook","slug":"redirect-entry-for-original-only-has-source-pa","errorCode":null,"errorMessage":"redirect entry for `{original}` only has source paths with `#` fragments\nThere must be an entry without the `#` fragment to determine the default destination.","messagePattern":"redirect entry for `(.+?)` only has source paths with `#` fragments\nThere must be an entry without the `#` fragment to determine the default destination\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/mdbook-html/src/html_handlebars/hbs_renderer.rs","lineNumber":261,"sourceCode":"        if redirects.is_empty() {\n            return Ok(());\n        }\n\n        debug!(\"Emitting redirects\");\n        let redirects = combine_fragment_redirects(redirects);\n\n        for (original, (dest, fragment_map)) in redirects {\n            // Note: all paths are relative to the build directory, so the\n            // leading slash in an absolute path means nothing (and would mess\n            // up `root.join(original)`).\n            let original = original.trim_start_matches('/');\n            let filename = root.join(original);\n            if filename.exists() {\n                // This redirect is handled by the in-page fragment mapper.\n                continue;\n            }\n            if dest.is_empty() {\n                bail!(\n                    \"redirect entry for `{original}` only has source paths with `#` fragments\\n\\\n                     There must be an entry without the `#` fragment to determine the default \\\n                     destination.\"\n                );\n            }\n            debug!(\"Redirecting \\\"{}\\\" → \\\"{}\\\"\", original, dest);\n            self.emit_redirect(handlebars, &filename, &dest, &fragment_map)?;\n        }\n\n        Ok(())\n    }\n\n    fn emit_redirect(\n        &self,\n        handlebars: &Handlebars<'_>,\n        original: &Path,\n        destination: &str,\n        fragment_map: &BTreeMap<String, String>,","sourceCodeStart":243,"sourceCodeEnd":279,"githubUrl":"https://github.com/rust-lang/mdBook/blob/dc21064fc21d955a0e1f67e65e336883c3e5260b/crates/mdbook-html/src/html_handlebars/hbs_renderer.rs#L243-L279","documentation":"mdBook's HTML renderer emits a redirect file for each entry in output.html.redirect. If the redirect key (the source path) contains a '#' fragment, the key alone doesn't identify a file to redirect from, so at least one entry for the same path WITHOUT the '#' fragment must exist to define the default destination page. This error is raised in emit_redirects when only fragment-bearing entries exist for a path.","triggerScenarios":"output.html.redirect contains only keys like \"path/to/page.html#section\" for a given original path, with no bare key like \"path/to/page.html\", and the bare filename does not exist on disk (so it isn't handled by the in-page fragment mapper).","commonSituations":"Users add fragment redirects (e.g. \"old.html#api\" -> \"new.html\") but forget the entry mapping the page itself; renaming chapters while updating old fragment links; hand-editing book.toml redirects section.","solutions":["Add a redirect entry without the '#' fragment for the same source path, e.g. \"old.html\" -> \"new.html\", alongside any fragment entries.","If the bare source file exists on disk in the build output, remove it or let the in-page fragment mapper handle it (the check is skipped when filename.exists()).","Remove the fragment-only redirect entries if they are no longer needed."],"exampleFix":"// before (book.toml)\n[output.html.redirect]\n\"old/page.html#section\" = \"new/page.html\"\n\n// after\n[output.html.redirect]\n\"old/page.html\" = \"new/page.html\"\n\"old/page.html#section\" = \"new/page.html#section\"","handlingStrategy":"validation","validationCode":"// Scan book.toml before building\nfor key in redirect_keys {\n    if key.contains('#') {\n        let bare = key.split('#').next().unwrap();\n        assert!(redirect_keys.contains(bare), \"add a redirect for {bare} without a fragment\");\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pair every fragment redirect with a bare-path redirect for the same file.","Keep redirects minimal and review [output.html.redirect] after restructures."],"tags":["config","redirects","mdbook"],"backgroundTag":"fragment-only-redirect-entry","analyzedSha":"dc21064fc21d955a0e1f67e65e336883c3e5260b","analyzedAt":"2026-09-01T10:15:12.134Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}