rust-lang/mdBook · error

[output.html.search.chapter] key

Error message

[output.html.search.chapter] key `{}` does not match any chapter paths

What it means

output.html.search.chapter-* settings let users tweak per-chapter search behavior keyed by chapter path. validate_chapter_config iterates those keys and fails if a key does not prefix-match any path in the chapter tree, catching typos before generating search files.

Solutions

  1. Fix the key in book.toml so it matches an actual chapter source path exactly as mdBook normalizes it (source-relative path).
  2. Remove the stale [output.html.search.chapter] entry for the renamed or deleted chapter.
  3. List the chapter tree (e.g. inspect SUMMARY.md) and copy the exact paths into the config.

Example fix

// before (book.toml)
[output.html.search.chapter.intro]
enable = false

// after (matching src/introduction.md)
[output.html.search.chapter.introduction]
enable = false
Defensive patterns

Strategy: validation

Validate before calling

// Verify search.chapter keys against SUMMARY source paths
for (const key of Object.keys(config['output.html.search.chapter'] || {})) {
  if (!sourcePaths.some(p => p === key || p.startsWith(key))) {
    throw new Error(`search.chapter key ${key} matches no chapter`);
  }
}

Prevention

When it happens

Trigger: Setting a key under [output.html.search.chapter] such as "some/chapter.md" (or a sub-table) whose path doesn't match (starts_with) any chapter's settings path computed from the SUMMARY tree.

Common situations: Typos in chapter paths; using absolute or OS-style paths instead of the source-relative form; renaming/moving a markdown file without updating the search.chapter key; enabling/disabling search on a deleted chapter.

Related errors


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

Appendix: source

Thrown at crates/mdbook-html/src/html_handlebars/search.rs:288

    Ok(json_contents)
}

fn settings_path(ch: &Chapter) -> &Path {
    ch.source_path
        .as_deref()
        .unwrap_or_else(|| ch.path.as_deref().unwrap())
}

fn validate_chapter_config(
    chapter_configs: &[(PathBuf, SearchChapterSettings)],
    chapter_trees: &[ChapterTree<'_>],
) -> Result<()> {
    for (path, _) in chapter_configs {
        let found = chapter_trees
            .iter()
            .any(|ct| settings_path(ct.chapter).starts_with(path));
        if !found {
            bail!(
                "[output.html.search.chapter] key `{}` does not match any chapter paths",
                path.display()
            );
        }
    }
    Ok(())
}

fn sort_search_config(
    map: &HashMap<String, SearchChapterSettings>,
) -> Vec<(PathBuf, SearchChapterSettings)> {
    let mut settings: Vec<_> = map
        .iter()
        .map(|(key, value)| (PathBuf::from(key), value.clone()))
        .collect();
    // Note: This is case-sensitive, and assumes the author uses the same case
    // as the actual filename.
    settings.sort_by(|a, b| a.0.cmp(&b.0));

View on GitHub (pinned to dc21064fc2)