{"record":{"id":"d91d1463f18f8e0f","repo":"rust-lang/mdBook","slug":"expected-bool-for-optional-key-e","errorCode":null,"errorMessage":"expected bool for `{optional_key}`: {e}","messagePattern":"expected bool for `(.+?)`: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/mdbook-driver/src/builtin_renderers/mod.rs","lineNumber":43,"sourceCode":"impl CmdRenderer {\n    /// Create a new `CmdRenderer` which will invoke the provided `cmd` string.\n    pub fn new(name: String, cmd: String) -> CmdRenderer {\n        CmdRenderer { name, cmd }\n    }\n}\n\nimpl Renderer for CmdRenderer {\n    fn name(&self) -> &str {\n        &self.name\n    }\n\n    fn render(&self, ctx: &RenderContext) -> Result<()> {\n        info!(\"Invoking the \\\"{}\\\" renderer\", self.name);\n\n        let optional_key = format!(\"output.{}.optional\", self.name);\n        let optional = match ctx.config.get(&optional_key) {\n            Ok(Some(value)) => value,\n            Err(e) => bail!(\"expected bool for `{optional_key}`: {e}\"),\n            Ok(None) => false,\n        };\n\n        let _ = fs::create_dir_all(&ctx.destination);\n\n        let mut cmd = crate::compose_command(&self.cmd, &ctx.root)?;\n        let mut child = match cmd\n            .stdin(Stdio::piped())\n            .stdout(Stdio::inherit())\n            .stderr(Stdio::inherit())\n            .current_dir(&ctx.destination)\n            .spawn()\n        {\n            Ok(c) => c,\n            Err(e) => {\n                return crate::handle_command_error(\n                    e, optional, \"output\", \"backend\", &self.name, &self.cmd,\n                );","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/rust-lang/mdBook/blob/dc21064fc21d955a0e1f67e65e336883c3e5260b/crates/mdbook-driver/src/builtin_renderers/mod.rs#L25-L61","documentation":"The command-based renderer reads `output.<name>.optional` expecting a boolean. If Config::get() fails (typically because the value exists but is not a bool, or get() rejects the key shape), it bails with this message.","triggerScenarios":"book.toml setting `output.<name>.optional = \"true\"` (string) or a number/table instead of a boolean; a config value that fails deserialization to bool.","commonSituations":"Quoted booleans in hand-edited book.toml; TOML users copying JSON-style config where true is quoted.","solutions":["Remove the quotes so the value is a real TOML boolean: optional = true","Delete the optional key if the backend is mandatory","Validate book.toml types (e.g. with a TOML linter) before building"],"exampleFix":"// before (book.toml)\n[output.mybackend]\noptional = \"true\"\n// after\n[output.mybackend]\noptional = true","handlingStrategy":"validation","validationCode":"let raw = toml::from_str::<toml::Value>(&std::fs::read_to_string(\"book.toml\")?)?;\nif let Some(o) = raw.get(\"optional\") {\n    assert!(o.is_bool(), \"output.<name>.optional must be an unquoted boolean\");\n}","typeGuard":null,"tryCatchPattern":"match ctx.config.get::<bool>(&optional_key) {\n    Ok(v) => v.unwrap_or(false),\n    Err(e) if e.to_string().contains(\"expected bool\") => {\n        eprintln!(\"fix {optional_key} in book.toml: must be true/false, unquoted\");\n        return Err(e);\n    }\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Write optional = true without quotes in book.toml","Remember TOML booleans are bare true/false, unlike JSON string \"true\"","Lint book.toml types before building"],"tags":["config","deserialization"],"backgroundTag":"invalid-config-key","analyzedSha":"dc21064fc21d955a0e1f67e65e336883c3e5260b","analyzedAt":"2026-09-01T10:15:12.134Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}