rust-lang/mdBook · critical
invalid key `{name}`
Error message
invalid key `{name}` What it means
Config::contains_key() is documented to work only with "output." and "preprocessor." prefixed names; any other name triggers a panic! (not a Result), because the method returns bool and has no error channel.
Source
Thrown at crates/mdbook-core/src/config.rs:222
.try_into()
.with_context(|| format!("Failed to deserialize `{name}`"))
})
.transpose()
}
/// Returns whether the config contains the given dotted key name.
///
/// The key can have dotted indices to access nested items (e.g.
/// `preprocessor.foo.bar` will check if that key is set in the config).
///
/// This can only access the `output` and `preprocessor` tables.
pub fn contains_key(&self, name: &str) -> bool {
if let Some(key) = name.strip_prefix("output.") {
self.output.read(key)
} else if let Some(key) = name.strip_prefix("preprocessor.") {
self.preprocessor.read(key)
} else {
panic!("invalid key `{name}`");
}
.is_some()
}
/// Returns the configuration for all preprocessors.
pub fn preprocessors<'de, T: Deserialize<'de>>(&self) -> Result<BTreeMap<String, T>> {
self.preprocessor
.clone()
.try_into()
.with_context(|| "Failed to read preprocessors")
}
/// Returns the configuration for all renderers.
pub fn outputs<'de, T: Deserialize<'de>>(&self) -> Result<BTreeMap<String, T>> {
self.output
.clone()
.try_into()
.with_context(|| "Failed to read renderers")View on GitHub (pinned to dc21064fc2)
Solutions
- Only call contains_key() with output.* or preprocessor.* keys
- Use config.get::<serde_json::Value>(name) for other sections, which returns a Result
- Check the relevant typed field directly (config.build, config.book, etc.)
Example fix
// before
let has = config.contains_key("build.dir"); // panics
// after
let has = matches!(config.get::<serde_json::Value>("build.build-dir"), Ok(Some(_))); Defensive patterns
Strategy: validation
Validate before calling
fn contains_key_safe(config: &Config, name: &str) -> bool {
if name.starts_with("output.") || name.starts_with("preprocessor.") {
config.contains_key(name)
} else {
matches!(config.get::<serde_json::Value>(name), Ok(Some(_)))
}
} Type guard
fn is_contains_key_safe(name: &str) -> bool {
name.starts_with("output.") || name.starts_with("preprocessor.")
} Prevention
- Never pass non-output/preprocessor keys to contains_key() — it panics rather than returning Err
- Use config.get::<Value>(name) for existence checks on other sections
- Prefer matches!(config.get(...), Ok(Some(_))) as a panic-free universal existence check
When it happens
Trigger: Calling config.contains_key("build.dir") or any key without the output./preprocessor. prefix; typo like "output" (no dot).
Common situations: Plugin authors checking for optional plugin config; code refactored from get() calls to contains_key() assuming it accepts all keys.
Related errors
- unable to get `{name}`, only `output` and `preprocessor` tab
- invalid key `{index}`
- The BookBuilder should always create a valid book. If you ar
- failed to get `{key}`: {e}
- expected bool for `{optional_key}`: {e}
AI-assisted analysis of rust-lang/mdBook@dc21064fc2 (2026-09-01).
Data as JSON: /api/errors/a505d215c3ce0b78.
Report an issue: GitHub.