{"record":{"id":"affa60ce990ed3d2","repo":"zeroclaw-labs/zeroclaw","slug":"step-step-number-phase-schema-validation-faile","errorCode":null,"errorMessage":"Step {step_number} {phase} schema validation failed: {reason}","messagePattern":"Step (.+?) (.+?) schema validation failed: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-runtime/src/sop/engine.rs","lineNumber":1999,"sourceCode":"        }\n        let Some(schema) = step\n            .schema\n            .as_ref()\n            .and_then(|schema| schema.output.as_ref())\n        else {\n            return Ok(());\n        };\n        schema::validate_value(schema, output).map_err(|e| e.to_string())\n    }\n\n    fn fail_step_schema_validation(\n        &mut self,\n        run_id: &str,\n        step_number: u32,\n        phase: &str,\n        reason: String,\n    ) -> Result<SopRunAction> {\n        let reason = format!(\"Step {step_number} {phase} schema validation failed: {reason}\");\n        self.record_transition_event(\n            run_id,\n            \"step_schema_reject\",\n            Some(reason.clone()),\n            ::serde_json::json!({\n                \"step\": step_number,\n                \"phase\": phase,\n            }),\n        );\n        ::zeroclaw_log::record!(\n            WARN,\n            ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)\n                .with_outcome(::zeroclaw_log::EventOutcome::Failure)\n                .with_attrs(::serde_json::json!({\n                    \"run_id\": run_id,\n                    \"step\": step_number,\n                    \"phase\": phase,\n                    \"reason\": reason,","sourceCodeStart":1981,"sourceCodeEnd":2017,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-runtime/src/sop/engine.rs#L1981-L2017","documentation":"The SOP engine validates each step's input and output JSON against the step's declared schema when `step_schema_enforce` is enabled. On mismatch, `fail_step_schema_validation` formats this reason, records a `step_schema_reject` transition event, logs a WARN failure event, and finishes the entire run with `SopRunStatus::Failed` — one invalid payload terminates the run.","triggerScenarios":"A step declares `schema.input`/`schema.output` and enforcement is on, but the submitted input or the model-produced output misses a required field, uses a wrong type, or violates a constraint; validate_step_input/validate_step_output return the validator message which lands in `{reason}`.","commonSituations":"LLM omits fields that look optional but are required; schemas tightened after SOPs were written; enum/const drift after prompt changes; nested objects with wrong shapes.","solutions":["Read the `{reason}` in the `step_schema_reject` event — it names the exact violation; adjust the producing prompt or caller so the payload conforms","Fix or relax the step's `schema.input`/`schema.output` in the SOP definition to match reality (drop unjustified `required` entries, correct types)","Add a repair loop: on rejection, re-prompt the model with the validation error before the run fails","If strictness is not needed, set `step_schema_enforce = false` for the engine"],"exampleFix":"// before: schema demands a field the model omits\n{\"schema\":{\"output\":{\"required\":[\"summary\",\"sources\"]}}}\n// payload: {\"summary\":\"...\"}\n\n// after: only require what the step truly needs\n{\"schema\":{\"output\":{\"required\":[\"summary\"],\"properties\":{\"sources\":{\"type\":\"array\"}}}}}\n// payload: {\"summary\":\"...\",\"sources\":[]}","handlingStrategy":"validation","validationCode":"fn required_fields_present(schema: &serde_json::Value, payload: &serde_json::Value) -> bool {\n    match schema.get(\"required\").and_then(|r| r.as_array()) {\n        None => true,\n        Some(req) => req\n            .iter()\n            .all(|k| payload.get(k.as_str().unwrap_or_default()).is_some()),\n    }\n}\n// run against the step's schema.input/schema.output before submitting each phase","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep step schemas minimal and cover them with fixture payloads in tests","On a step_schema_reject event, feed the reason back to the model and repair the payload instead of letting the run fail","Pin prompts and schemas together in the SOP definition so they cannot drift independently"],"tags":["rust","sop","json-schema","validation","llm-output","run-failed"],"backgroundTag":"schema-validation-failed","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}