rust-lang/mdBook · error

redirect entry for `{original}` only has source paths with `

Error message

redirect entry for `{original}` only has source paths with `#` fragments
There must be an entry without the `#` fragment to determine the default destination.

What it means

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.

Source

Thrown at crates/mdbook-html/src/html_handlebars/hbs_renderer.rs:261

        if redirects.is_empty() {
            return Ok(());
        }

        debug!("Emitting redirects");
        let redirects = combine_fragment_redirects(redirects);

        for (original, (dest, fragment_map)) in redirects {
            // Note: all paths are relative to the build directory, so the
            // leading slash in an absolute path means nothing (and would mess
            // up `root.join(original)`).
            let original = original.trim_start_matches('/');
            let filename = root.join(original);
            if filename.exists() {
                // This redirect is handled by the in-page fragment mapper.
                continue;
            }
            if dest.is_empty() {
                bail!(
                    "redirect entry for `{original}` only has source paths with `#` fragments\n\
                     There must be an entry without the `#` fragment to determine the default \
                     destination."
                );
            }
            debug!("Redirecting \"{}\" → \"{}\"", original, dest);
            self.emit_redirect(handlebars, &filename, &dest, &fragment_map)?;
        }

        Ok(())
    }

    fn emit_redirect(
        &self,
        handlebars: &Handlebars<'_>,
        original: &Path,
        destination: &str,
        fragment_map: &BTreeMap<String, String>,

View on GitHub (pinned to dc21064fc2)

Solutions

  1. Add a redirect entry without the '#' fragment for the same source path, e.g. "old.html" -> "new.html", alongside any fragment entries.
  2. 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()).
  3. Remove the fragment-only redirect entries if they are no longer needed.

Example fix

// before (book.toml)
[output.html.redirect]
"old/page.html#section" = "new/page.html"

// after
[output.html.redirect]
"old/page.html" = "new/page.html"
"old/page.html#section" = "new/page.html#section"
Defensive patterns

Strategy: validation

Validate before calling

// Scan book.toml before building
for key in redirect_keys {
    if key.contains('#') {
        let bare = key.split('#').next().unwrap();
        assert!(redirect_keys.contains(bare), "add a redirect for {bare} without a fragment");
    }
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of rust-lang/mdBook@dc21064fc2 (2026-09-01). Data as JSON: /api/errors/2166d0716e3bf2f6. Report an issue: GitHub.