rust-lang/mdBook · error
invalid key `{index}`
Error message
invalid key `{index}` What it means
Config::set() only accepts keys whose index begins with "output." or "preprocessor." (plus special-cased root/rust keys handled earlier in the function). Setting any other top-level value is rejected to avoid corrupting nested config tables via dotted-string mutation.
Source
Thrown at crates/mdbook-core/src/config.rs:301
self.build = value.try_into()?;
} else if index == "rust" {
self.rust = value.try_into()?;
} else if index == "output" {
self.output = value;
} else if index == "preprocessor" {
self.preprocessor = value;
} else if let Some(key) = index.strip_prefix("book.") {
self.book.update_value(key, value)?;
} else if let Some(key) = index.strip_prefix("build.") {
self.build.update_value(key, value)?;
} else if let Some(key) = index.strip_prefix("rust.") {
self.rust.update_value(key, value)?;
} else if let Some(key) = index.strip_prefix("output.") {
self.output.update_value(key, value)?;
} else if let Some(key) = index.strip_prefix("preprocessor.") {
self.preprocessor.update_value(key, value)?;
} else {
bail!("invalid key `{index}`");
}
Ok(())
}
}
fn parse_env(key: &str) -> Option<String> {
key.strip_prefix("MDBOOK_")
.map(|key| key.to_lowercase().replace("__", ".").replace('_', "-"))
}
/// Configuration options which are specific to the book and required for
/// loading it from disk.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(default, rename_all = "kebab-case", deny_unknown_fields)]
#[non_exhaustive]
pub struct BookConfig {
/// The book's title.View on GitHub (pinned to dc21064fc2)
Solutions
- Mutate the typed structs directly (config.book.title = ..., config.build) instead of set()
- Only use set() for output.* / preprocessor.* values
- For env overrides, use a supported section (build, rust, output, preprocessor) in the MDBOOK_ variable name
Example fix
// before
config.set("book.title", "My Book")?;
// after
config.book.title = Some("My Book".to_string()); Defensive patterns
Strategy: validation
Validate before calling
fn settable(key: &str) -> bool {
key.starts_with("output.") || key.starts_with("preprocessor.")
} Type guard
fn is_settable_index(index: &str) -> bool {
index.starts_with("output.") || index.starts_with("preprocessor.")
} Try / catch
if let Err(e) = config.set(key, value) {
if e.to_string().starts_with("invalid key") {
// mutate typed fields directly instead
} else { return Err(e); }
} Prevention
- Use set() only for output.*/preprocessor.* values
- Prefer mutating config.book/config.build/config.rust fields directly
- Check MDBOOK_ env var section names (build, rust, output, preprocessor) when using env overrides
When it happens
Trigger: Calling config.set("build.build-dir", value) or any non-output/preprocessor dotted key; env-driven updates (update_from_env) with MDBOOK_<SECTION>__<KEY> variables whose section is not output/preprocessor/rust.
Common situations: Programmatic config editing scripts and MDBOOK_* environment variable overrides for sections like MDBOOK_BOOK__AUTHORS (note case: env keys are lowercased; unsupported sections hit this).
Related errors
- unable to get `{name}`, only `output` and `preprocessor` tab
- invalid key `{name}`
- failed to get `{key}`: {e}
- expected bool for `{optional_key}`: {e}
- redirect entry for `{original}` only has source paths with `
AI-assisted analysis of rust-lang/mdBook@dc21064fc2 (2026-09-01).
Data as JSON: /api/errors/cf3eef49aa7aa25a.
Report an issue: GitHub.