{"record":{"id":"f1278249da564fde","repo":"openai/codex","slug":"generated-input-schema-for-name-should-parse-e","errorCode":null,"errorMessage":"generated input schema for {name} should parse: {err}","messagePattern":"generated input schema for (.+?) should parse: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"codex-rs/ext/skills/src/tools/mod.rs","lineNumber":260,"sourceCode":"            SkillSourceKind::Host | SkillSourceKind::Orchestrator | SkillSourceKind::Custom(_) => {\n                None\n            }\n        }\n    }\n}\n\nfn skill_tool_name(name: &str) -> ToolName {\n    ToolName::namespaced(SKILLS_NAMESPACE, name)\n}\n\nfn skill_function_tool<I: JsonSchema, O: JsonSchema>(name: &str, description: &str) -> ToolSpec {\n    let tool = ResponsesApiTool {\n        name: name.to_string(),\n        description: description.to_string(),\n        strict: false,\n        defer_loading: None,\n        parameters: parse_tool_input_schema(&schema::input_schema_for::<I>())\n            .unwrap_or_else(|err| panic!(\"generated input schema for {name} should parse: {err}\")),\n        output_schema: Some(schema::output_schema_for::<O>()),\n    };\n\n    ToolSpec::Namespace(ResponsesApiNamespace {\n        name: SKILLS_NAMESPACE.to_string(),\n        description: default_namespace_description(SKILLS_NAMESPACE),\n        tools: vec![ResponsesApiNamespaceTool::Function(tool)],\n    })\n}\n\nfn parse_args<T: for<'de> Deserialize<'de>>(call: &ToolCall) -> Result<T, FunctionCallError> {\n    let arguments = call.function_arguments()?;\n    let value = if arguments.trim().is_empty() {\n        Value::Object(serde_json::Map::new())\n    } else {\n        serde_json::from_str(arguments)\n            .map_err(|err| FunctionCallError::RespondToModel(err.to_string()))?\n    };","sourceCodeStart":242,"sourceCodeEnd":278,"githubUrl":"https://github.com/openai/codex/blob/339751715c64496cb86246bfb3935f40e309dd3d/codex-rs/ext/skills/src/tools/mod.rs#L242-L278","documentation":"This panic fires inside skill_function_tool (codex-rs/ext/skills/src/tools/mod.rs:253-262): the JSON Schema that schemars derives for the tool input type I is passed to parse_tool_input_schema, which converts it into the strict Responses API tool-parameter format. The unwrap_or_else(panic) encodes an internal invariant - a schema derived from a Rust JsonSchema type should always parse. When it fires, a skill tool's input type produced a schema shape the parser rejects (for example a non-object root such as a bare Vec/tuple alias, or keywords the strict parser cannot normalize), and it happens at tool registration time, before any model call.","triggerScenarios":"Registering a skills-extension tool whose input type's derived schema fails parse_tool_input_schema - typically a non-object root schema (type alias of Vec<T>, a primitive, or a tuple struct), or an exotic construct emitted after a schemars upgrade. It panics during startup or first skill listing, deterministically per build.","commonSituations":"A new skill tool defined with 'type MyArgs = Vec<String>' instead of a struct with named fields; a schemars major-version bump changing emitted schema shapes; a patched or forked schemars in the lockfile.","solutions":["Make the tool input type a struct with named fields so the root schema is an object (struct SearchArgs { terms: Vec<String> }) instead of a Vec/tuple/primitive alias","Reproduce in a unit test: run schema::input_schema_for::<I>() through parse_tool_input_schema and read the embedded {err} - it names the offending construct","If it appeared after a dependency change, inspect Cargo.lock for schemars/serde_json drift and pin a known-good version","If the type is legitimately object-shaped and it still fails, file a codex-rs issue - generated schemas failing to parse are a library bug"],"exampleFix":"// before\ntype SearchArgs = Vec<String>; // array root schema -> panic\nskill_function_tool::<SearchArgs, SearchOut>(\"search\", \"...\")\n\n// after\n#[derive(Deserialize, JsonSchema)]\nstruct SearchArgs {\n    /// One or more search terms.\n    terms: Vec<String>,\n}\nskill_function_tool::<SearchArgs, SearchOut>(\"search\", \"...\")","handlingStrategy":"try-catch","validationCode":"// unit test per tool: construction panics if the generated schema does not parse\n#[test]\nfn my_tool_schema_parses() {\n    let spec = skill_function_tool::<MyArgs, MyOut>(\"my_tool\", \"desc\");\n    assert!(matches!(spec, ToolSpec::Namespace(_)));\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":"// keep registration panics from taking down a long-lived host:\nlet spec = std::panic::catch_unwind(|| {\n    skill_function_tool::<MyArgs, MyOut>(\"my_tool\", \"desc\")\n});\nif let Err(panic) = spec {\n    tracing::error!(?panic, \"skill tool registration failed; skipping tool\");\n}","preventionTips":["Always define tool args as structs with named fields - never bare Vec/Option/tuple aliases","Add a unit test per tool that constructs its ToolSpec so the panic becomes a red test, not a crashed process","Review Cargo.lock diffs for schemars when this appears out of nowhere","Read the panic text: {name} names the offending tool, {err} names the offending schema construct"],"tags":["skills","json-schema","schemars","panic","tool-registration","rust"],"backgroundTag":"json-schema-generation-failed","analyzedSha":"339751715c64496cb86246bfb3935f40e309dd3d","analyzedAt":"2026-08-25T05:35:09.876Z","schemaVersion":2},"datasetVersion":"2026-08-25T06:17:31.827Z"}