{"record":{"id":"2b64a90edb5e7a66","repo":"odysseus-dev/odysseus","slug":"request-parsing-error-e","errorCode":null,"errorMessage":"Request parsing error: {e}","messagePattern":"Request parsing error: (.+?)","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"routes/chat_routes.py","lineNumber":880,"sourceCode":"            \"endpoint_label\": actual_route.get(\"endpoint_label\"),\n        }\n\n    # ------------------------------------------------------------------ #\n    # POST /api/chat_stream\n    # ------------------------------------------------------------------ #\n    @router.post(\"/api/chat_stream\")\n    async def chat_stream(request: Request) -> StreamingResponse:\n        body = None\n        try:\n            if request.headers.get(\"content-type\", \"\").startswith(\"application/json\"):\n                try:\n                    body = await request.json()\n                except json.JSONDecodeError as e:\n                    raise HTTPException(400, f\"Invalid JSON: {e}\")\n        except HTTPException:\n            raise\n        except Exception as e:\n            raise HTTPException(400, f\"Request parsing error: {e}\")\n\n        _set_user_time_from_request(request)\n\n        form_data = await request.form()\n        message = form_data.get(\"message\")\n        session = form_data.get(\"session\")\n        attachments = form_data.get(\"attachments\")\n        use_web = form_data.get(\"use_web\")\n        use_research = form_data.get(\"use_research\")\n        time_filter = form_data.get(\"time_filter\")\n        preset_id = form_data.get(\"preset_id\")\n        selected_endpoint_id = str(\n            form_data.get(\"selected_endpoint_id\")\n            or (body or {}).get(\"selected_endpoint_id\")\n            or \"\"\n        ).strip()\n        # Issue #3229: API callers send JSON, not FormData.  Read from the\n        # JSON body as fallback so callers who send {\"allow_bash\": true}","sourceCodeStart":862,"sourceCodeEnd":898,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/chat_routes.py#L862-L898","documentation":"Catch-all for any non-JSONDecodeError exception while reading/parsing the streaming chat request (the outer try wraps the content-type check and body read). It converts unexpected transport-level failures into a 400 with the underlying exception text.","triggerScenarios":"Body stream read errors (client disconnected mid-upload), unicode decode failures on non-UTF-8 bytes, malformed multipart framing when the endpoint falls through to request.form(), or starlette raising on malformed chunked encoding.","commonSituations":"Client aborted the upload; wrong charset declared; a debugging proxy re-encoding the request; framework-level incompatibilities after a Starlette/FastAPI upgrade changing body-reading semantics.","solutions":["Read the exception text in the 400 response — it names the actual underlying failure","Reproduce with a minimal curl request to rule out client/proxy mangling","Ensure the client sends UTF-8 and does not abort before the body completes"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { await send(); } catch (e) {\n  if (e.status === 400 && /parsing error/i.test(e.message)) { logUnderlying(e.message); inspectProxyAndEncoding(); }\n}","preventionTips":["Send UTF-8 bodies and complete requests (don't abort mid-upload)","Bypass suspect proxies when diagnosing to isolate where the body gets corrupted"],"tags":["json","request-parsing","fastapi","client-error"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}