{"record":{"id":"fb2ad1777a621b03","repo":"openai/codex","slug":"root-tool-schema-must-be-an-object","errorCode":null,"errorMessage":"root tool schema must be an object","messagePattern":"root tool schema must be an object","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"codex-rs/ext/skills/src/tools/schema.rs","lineNumber":25,"sourceCode":"    schema_for::<T>(/*option_add_null_type*/ false)\n}\n\npub(super) fn output_schema_for<T: JsonSchema>() -> Value {\n    schema_for::<T>(/*option_add_null_type*/ true)\n}\n\nfn schema_for<T: JsonSchema>(option_add_null_type: bool) -> Value {\n    let schema = SchemaSettings::draft2019_09()\n        .with(|settings| {\n            settings.inline_subschemas = true;\n            settings.option_add_null_type = option_add_null_type;\n        })\n        .into_generator()\n        .into_root_schema_for::<T>();\n    let schema_value = serde_json::to_value(schema)\n        .unwrap_or_else(|err| panic!(\"generated skill tool schema should serialize: {err}\"));\n    let Value::Object(mut schema_object) = schema_value else {\n        unreachable!(\"root tool schema must be an object\");\n    };\n\n    let mut tool_schema = Map::new();\n    for key in [\n        \"properties\",\n        \"required\",\n        \"type\",\n        \"additionalProperties\",\n        \"$defs\",\n        \"definitions\",\n    ] {\n        if let Some(value) = schema_object.remove(key) {\n            tool_schema.insert(key.to_string(), value);\n        }\n    }\n    Value::Object(tool_schema)\n}\n","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/openai/codex/blob/339751715c64496cb86246bfb3935f40e309dd3d/codex-rs/ext/skills/src/tools/schema.rs#L7-L43","documentation":"This unreachable! in schema_for (codex-rs/ext/skills/src/tools/schema.rs:24-26) guards the destructure of the serialized root schema into a JSON object. schemars root schemas are always objects (a map of $schema/title/type/properties/...), so the branch is considered impossible. It would fire only if the JsonSchema implementation for the tool type - or a future schemars major version - emitted a non-object root, for example a boolean schema (true/false) for an unconstrained or never-valid type, or an array.","triggerScenarios":"A tool input type whose derived root schema serializes to true/false/array instead of an object - possible with custom JsonSchema impls, Schema-typed wrappers, or a schemars upgrade that starts emitting boolean root schemas. Fires during skill tool registration.","commonSituations":"Upgrading schemars across a major version that changes root-schema emission; a hand-written JsonSchema impl returning Schema::Bool(true); practically unseen on stock codex-rs builds.","solutions":["Inspect the offending tool type: make it a plain struct (or enum with struct variants) deriving JsonSchema, not a type backed by Schema::Bool or a custom impl","Pin schemars to the version codex-rs was built against if the panic appeared after a dependency refresh","Run cargo clean and rebuild to rule out stale artifacts","If reproducible on a clean build with stock deps, report upstream to codex-rs with the type definition"],"exampleFix":"// before\n#[derive(JsonSchema)]\nstruct MySchemaWrapper(schemars::schema::Schema); // root may serialize to true/false\n\n// after\n#[derive(Deserialize, JsonSchema)]\nstruct MyArgs {\n    query: String,\n}","handlingStrategy":"try-catch","validationCode":"// inside the skills crate: assert an object root per tool before shipping\n#[test]\nfn tool_schema_has_object_root() {\n    assert!(matches!(\n        schema::input_schema_for::<MyArgs>(),\n        serde_json::Value::Object(_)\n    ));\n}","typeGuard":"fn tool_args_have_object_root<I: schemars::JsonSchema>() -> bool {\n    matches!(\n        serde_json::to_value(schemars::schema_for!(I)).ok(),\n        Some(serde_json::Value::Object(_))\n    )\n}","tryCatchPattern":"let built = std::panic::catch_unwind(|| {\n    skill_function_tool::<MyArgs, MyOut>(\"my_tool\", \"desc\")\n});\nif built.is_err() {\n    tracing::error!(\"skill schema generation panicked; skipping tool\");\n}","preventionTips":["Derive JsonSchema on plain structs; avoid custom JsonSchema impls and Schema-typed wrappers for tool args","Lock schemars to the version codex-rs ships with","A Value::Object root assertion per tool turns this unreachable into a build-time failure"],"tags":["skills","json-schema","schemars","panic","unreachable","rust"],"backgroundTag":"json-schema-generation-failed","analyzedSha":"339751715c64496cb86246bfb3935f40e309dd3d","analyzedAt":"2026-08-25T05:35:09.876Z","schemaVersion":2},"datasetVersion":"2026-08-25T06:17:31.827Z"}