zeroclaw-labs/zeroclaw · error

Step {step_number} {phase} schema validation failed: {reason

Error message

Step {step_number} {phase} schema validation failed: {reason}

What it means

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.

Source

Thrown at crates/zeroclaw-runtime/src/sop/engine.rs:1999

        }
        let Some(schema) = step
            .schema
            .as_ref()
            .and_then(|schema| schema.output.as_ref())
        else {
            return Ok(());
        };
        schema::validate_value(schema, output).map_err(|e| e.to_string())
    }

    fn fail_step_schema_validation(
        &mut self,
        run_id: &str,
        step_number: u32,
        phase: &str,
        reason: String,
    ) -> Result<SopRunAction> {
        let reason = format!("Step {step_number} {phase} schema validation failed: {reason}");
        self.record_transition_event(
            run_id,
            "step_schema_reject",
            Some(reason.clone()),
            ::serde_json::json!({
                "step": step_number,
                "phase": phase,
            }),
        );
        ::zeroclaw_log::record!(
            WARN,
            ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)
                .with_outcome(::zeroclaw_log::EventOutcome::Failure)
                .with_attrs(::serde_json::json!({
                    "run_id": run_id,
                    "step": step_number,
                    "phase": phase,
                    "reason": reason,

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Read the `{reason}` in the `step_schema_reject` event — it names the exact violation; adjust the producing prompt or caller so the payload conforms
  2. Fix or relax the step's `schema.input`/`schema.output` in the SOP definition to match reality (drop unjustified `required` entries, correct types)
  3. Add a repair loop: on rejection, re-prompt the model with the validation error before the run fails
  4. If strictness is not needed, set `step_schema_enforce = false` for the engine

Example fix

// before: schema demands a field the model omits
{"schema":{"output":{"required":["summary","sources"]}}}
// payload: {"summary":"..."}

// after: only require what the step truly needs
{"schema":{"output":{"required":["summary"],"properties":{"sources":{"type":"array"}}}}}
// payload: {"summary":"...","sources":[]}
Defensive patterns

Strategy: validation

Validate before calling

fn required_fields_present(schema: &serde_json::Value, payload: &serde_json::Value) -> bool {
    match schema.get("required").and_then(|r| r.as_array()) {
        None => true,
        Some(req) => req
            .iter()
            .all(|k| payload.get(k.as_str().unwrap_or_default()).is_some()),
    }
}
// run against the step's schema.input/schema.output before submitting each phase

Prevention

When it happens

Trigger: 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}`.

Common situations: 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.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/affa60ce990ed3d2. Report an issue: GitHub.