{"record":{"id":"f714bcf8e925a1a2","repo":"shareAI-lab/learn-claude-code","slug":"workflow-agent-returned-invalid-json","errorCode":null,"errorMessage":"workflow agent returned invalid JSON","messagePattern":"workflow agent returned invalid JSON","errorType":"exception","errorClass":"WorkflowInputError","httpStatus":null,"severity":"error","filePath":"s16_workflow_runtime/code.py","lineNumber":274,"sourceCode":"    if stripped.startswith(\"```\"):\n        lines = stripped.splitlines()\n        lines = lines[1:] if lines else lines\n        if lines and lines[-1].strip() == \"```\":\n            lines = lines[:-1]\n        stripped = \"\\n\".join(lines).strip()\n    try:\n        return json.loads(stripped)\n    except json.JSONDecodeError:\n        decoder = json.JSONDecoder()\n        for position, character in enumerate(stripped):\n            if character != \"{\":\n                continue\n            try:\n                value, _ = decoder.raw_decode(stripped[position:])\n            except json.JSONDecodeError:\n                continue\n            return value\n        raise WorkflowInputError(\"workflow agent returned invalid JSON\")\n\n\nclass AnthropicAgentRunner:\n    \"\"\"Run workflow agents through the same API client as the host.\"\"\"\n\n    def __init__(self, client, model):\n        self.client = client\n        self.model = model\n\n    def run(self, prompt, schema=None, label=None):\n        request = prompt\n        if schema is not None:\n            request += (\n                \"\\n\\nReturn only one JSON object matching this schema:\\n\"\n                + json.dumps(schema, ensure_ascii=True, sort_keys=True)\n            )\n        response = self.client.messages.create(\n            model=self.model,","sourceCodeStart":256,"sourceCodeEnd":292,"githubUrl":"https://github.com/shareAI-lab/learn-claude-code/blob/985456f4adea6f4df8fbad4112245dbd97444eae/s16_workflow_runtime/code.py#L256-L292","documentation":"The workflow runtime asks each agent() response to be JSON and parses it leniently: direct json.loads, then a scan for the first '{' that raw_decodes successfully. Only when every candidate object fails to parse does it raise WorkflowInputError('workflow agent returned invalid JSON'). This means the model returned prose, an error page, or malformed JSON with no recoverable object.","triggerScenarios":"The agent replies with natural language ('Sure, here is the result...') instead of JSON. The response is truncated mid-object (max_tokens hit). The only braces present are inside strings/comments that never close, so raw_decode fails at each position. An empty response after stripping.","commonSituations":"Prompt/schema drift after a model upgrade where instructions to 'answer in JSON' stop being followed. Token limits truncating output. Proxies or gateways returning HTML error pages instead of model output.","solutions":["Strengthen the prompt: state 'respond with only a JSON object' and show the expected shape; prefer passing schema= so it is encoded into the request","Raise max_tokens / reduce requested output size so JSON is not truncated","Retry the agent() call — transient non-JSON replies often succeed on retry; catch WorkflowInputError and re-run with the parse failure fed back"],"exampleFix":"# before\nresult = state.agent(\"Summarize this document.\")\n\n# after\nresult = state.agent(\n    \"Summarize this document. Respond with ONLY a JSON object like {\\\"summary\\\": string, \\\"topics\\\": [string]}.\",\n    schema={\"type\": \"object\", \"properties\": {\"summary\": {\"type\": \"string\"}, \"topics\": {\"type\": \"array\", \"items\": {\"type\": \"string\"}}}, \"required\": [\"summary\"]},\n)","handlingStrategy":"retry","validationCode":"import json\n\ndef looks_like_json_object(text: str) -> bool:\n    stripped = text.strip()\n    return stripped.startswith(\"{\") and \"}\" in stripped\n\n# advisory only: the runtime's brace-scan is more lenient than this","typeGuard":null,"tryCatchPattern":"last_exc = None\nfor attempt in range(2):\n    try:\n        result = state.agent(prompt, schema=schema, label=\"extract\")\n        break\n    except WorkflowInputError as exc:\n        if \"invalid JSON\" not in str(exc):\n            raise\n        last_exc = exc\n        prompt += \"\\nIMPORTANT: reply with ONLY a valid JSON object, no prose.\"\nelse:\n    raise last_exc","preventionTips":["Always pass schema= and instruct JSON-only replies","Size max_tokens so JSON output cannot truncate","Feed the parse failure back into the retry prompt"],"tags":["workflow","agent","json","llm-output","parsing"],"backgroundTag":null,"analyzedSha":"985456f4adea6f4df8fbad4112245dbd97444eae","analyzedAt":"2026-08-14T22:02:26.028Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}