{"record":{"id":"31aa7d05ec9e542f","repo":"rust-lang/mdBook","slug":"redirect-found-for-existing-chapter-at-path-ei","errorCode":null,"errorMessage":"redirect found for existing chapter at `{path}`\nEither delete the redirect or remove the chapter.","messagePattern":"redirect found for existing chapter at `(.+?)`\nEither delete the redirect or remove the chapter\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/mdbook-html/src/html_handlebars/hbs_renderer.rs","lineNumber":680,"sourceCode":"        } else {\n            let e = combined.entry(original.to_string()).or_default();\n            e.0 = new.clone();\n        }\n    }\n    combined\n}\n\n/// Collects fragment redirects for an existing page.\n///\n/// The returned map has keys like `#foo` and the value is the new destination\n/// path or URL.\nfn collect_redirects_for_path(\n    path: &Path,\n    redirects: &HashMap<String, String>,\n) -> Result<BTreeMap<String, String>> {\n    let path = format!(\"/{}\", path.to_url_path());\n    if redirects.contains_key(&path) {\n        bail!(\n            \"redirect found for existing chapter at `{path}`\\n\\\n            Either delete the redirect or remove the chapter.\"\n        );\n    }\n\n    let key_prefix = format!(\"{path}#\");\n    let map = redirects\n        .iter()\n        .filter_map(|(source, dest)| {\n            source\n                .strip_prefix(&key_prefix)\n                .map(|fragment| (format!(\"#{fragment}\"), dest.to_string()))\n        })\n        .collect();\n    Ok(map)\n}\n","sourceCodeStart":662,"sourceCodeEnd":697,"githubUrl":"https://github.com/rust-lang/mdBook/blob/dc21064fc21d955a0e1f67e65e336883c3e5260b/crates/mdbook-html/src/html_handlebars/hbs_renderer.rs#L662-L697","documentation":"collect_redirects_for_path checks, before rendering a chapter, whether output.html.redirect defines a redirect whose source equals the chapter's URL path. Redirects and real chapters must be mutually exclusive; if a redirect would shadow an existing chapter, the build fails with this error to make the conflict explicit.","triggerScenarios":"A SUMMARY.md chapter is rendered at path P while [output.html.redirect] contains a key \"/P\" (or \"/P#fragment\" via the key_prefix check) for the same path — e.g. a chapter was re-added after a redirect stub was configured for its old location.","commonSituations":"Restructuring a book: a page was renamed and a redirect added, then a chapter was moved back to the original path without deleting the redirect; merging branches where one added a redirect and another restored the chapter.","solutions":["Delete the conflicting entry from [output.html.redirect] in book.toml.","Remove or rename the chapter at that path in SUMMARY.md if the redirect is the intended behavior.","Adjust the redirect key to a different source path if both pages should exist."],"exampleFix":"// before (book.toml)\n[output.html.redirect]\n\"guide/intro.html\" = \"guide/index.html\"\n// while src/guide/intro.md still exists in SUMMARY.md\n\n// after: remove the redirect line\n[output.html.redirect]","handlingStrategy":"validation","validationCode":"// Ensure no redirect source matches a SUMMARY chapter path\nfor (const redirect of Object.keys(config['output.html.redirect'] || {})) {\n  if (chapterPaths.includes(redirect)) {\n    throw new Error(`redirect ${redirect} conflicts with an existing chapter`);\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["When restoring a chapter at an old path, delete its redirect entry in the same commit.","Keep redirects and SUMMARY.md changes in the same PR to avoid drift."],"tags":["config","redirects","conflict","mdbook"],"backgroundTag":"redirect-conflicts-with-existing-chapter","analyzedSha":"dc21064fc21d955a0e1f67e65e336883c3e5260b","analyzedAt":"2026-09-01T10:15:12.134Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}