{"record":{"id":"1c9e56eae2608715","repo":"shareAI-lab/learn-claude-code","slug":"agent-schema-invalid-output-err","errorCode":null,"errorMessage":"agent({{schema}}) invalid output: {err}","messagePattern":"agent\\((.+?)\\}\\) invalid output: (.+?)","errorType":"validation","errorClass":"WorkflowInputError","httpStatus":null,"severity":"error","filePath":"s16_workflow_runtime/code.py","lineNumber":496,"sourceCode":"                self.runner.run, prompt, schema, label\n            )\n            result = run.value\n            tokens = run.tokens\n\n        if schema is not None:\n            ok, err = SimpleJsonSchema(schema).validate(result)\n            if not ok:\n                retry = await asyncio.to_thread(\n                    self.runner.run,\n                    prompt + \"\\n\\nReturn valid JSON.\",\n                    schema,\n                    label,\n                )\n                result = retry.value\n                tokens += retry.tokens\n                ok, err = SimpleJsonSchema(schema).validate(result)\n                if not ok:\n                    raise WorkflowInputError(f\"agent({{schema}}) invalid output: {err}\")\n\n        self.budget.add(tokens)\n        self.task.usage[\"agents\"] += 1\n        self.task.usage[\"tokens\"] += tokens\n        self.journal.record(key, result)\n        self.task.progress_event(\"workflow_agent\", label=label,\n                                 phase=phase or self._phase, status=\"done\")\n        return result\n\n    async def parallel(self, thunks):\n        \"\"\"BARRIER: run all thunks concurrently and fail if any thunk fails.\"\"\"\n        return await asyncio.gather(*[thunk() for thunk in thunks])\n\n    async def pipeline(self, items, *stages):\n        \"\"\"Per-item staged flow, NO barrier between stages: item A can be in\n        stage 3 while item B is still in stage 1. Each stage gets\n        (prev_result, original_item, index). A throwing stage fails the workflow.\"\"\"\n        async def run_item(item, idx):","sourceCodeStart":478,"sourceCodeEnd":514,"githubUrl":"https://github.com/shareAI-lab/learn-claude-code/blob/985456f4adea6f4df8fbad4112245dbd97444eae/s16_workflow_runtime/code.py#L478-L514","documentation":"When agent() is called with a schema, the runner is forced into StructuredOutput mode and the result is validated with SimpleJsonSchema. On the first failure the model is retried once with 'Return valid JSON.' appended; if the retry also fails validation, WorkflowInputError with the schema error text is raised. Nothing is journaled for the failed call, so a later retry re-runs the agent from scratch.","triggerScenarios":"ctx.agent(prompt, schema=...) where the model returns JSON that violates the schema twice in a row — e.g. missing required fields, wrong types, extra fields when additionalProperties is false, or truncated output.","commonSituations":"Overly strict or nested schemas the model can't satisfy; required fields the prompt never asks for; long outputs hitting max tokens and truncating the JSON; weak models on structured output.","solutions":["Simplify the schema: fewer required fields, flatter structure, relax types (e.g. accept string|number), allow additional properties.","Make the prompt explicitly request every required field with the exact names and types the schema expects.","Increase the runner's max output tokens so the JSON is not truncated.","Re-run the workflow — the failed attempt was not journaled, so the agent gets two fresh attempts under the improved schema."],"exampleFix":"# before\nschema = {\"type\": \"object\", \"required\": [\"findings\", \"severity\", \"citations\"],\n           \"additionalProperties\": False}\n\n# after\nschema = {\"type\": \"object\", \"required\": [\"findings\"],\n           \"properties\": {\"findings\": {\"type\": \"array\"}}}","handlingStrategy":"retry","validationCode":"from s16_workflow_runtime import SimpleJsonSchema\ndef schema_is_model_feasible(schema) -> bool:\n    # cheap sanity: object root, <= 5 required keys, no deep nesting\n    props = schema.get(\"properties\", {})\n    req = schema.get(\"required\", [])\n    return schema.get(\"type\") == \"object\" and len(req) <= 5 and len(props) <= 10","typeGuard":null,"tryCatchPattern":"last = None\nfor attempt in range(3):\n    try:\n        return await ctx.agent(prompt, schema=S, label=lbl)\n    except WorkflowInputError as e:\n        if \"invalid output\" not in str(e):\n            raise\n        last = e\n        prompt += \"\\nRespond ONLY with JSON matching: \" + json.dumps(S)\nraise last","preventionTips":["Keep schemas flat with few required fields; make optional what the model may omit.","Spell out every required field name and type in the prompt itself.","Raise the runner's max output tokens so JSON never truncates."],"tags":["workflow","schema-validation","structured-output","llm","retry"],"backgroundTag":null,"analyzedSha":"985456f4adea6f4df8fbad4112245dbd97444eae","analyzedAt":"2026-08-14T22:02:26.028Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}