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

  1. Mutate the typed structs directly (config.book.title = ..., config.build) instead of set()
  2. Only use set() for output.* / preprocessor.* values
  3. 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

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


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