{"record":{"id":"d6cfce6ca49495d6","repo":"odysseus-dev/odysseus","slug":"invalid-json","errorCode":null,"errorMessage":"Invalid JSON","messagePattern":"Invalid JSON","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"routes/backup_routes.py","lineNumber":71,"sourceCode":"            \"preferences\": preferences,\n        }\n\n        filename = f\"odysseus_backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json\"\n        return Response(\n            content=json.dumps(export_data, indent=2, ensure_ascii=False),\n            media_type=\"application/json\",\n            headers={\"Content-Disposition\": f\"attachment; filename={filename}\"},\n        )\n\n    @router.post(\"/api/import\")\n    async def import_data(request: Request):\n        \"\"\"Import user data from a previously exported JSON file. Merges with existing data.\"\"\"\n        require_admin(request)\n        user = get_current_user(request)\n        try:\n            body = await request.json()\n        except Exception:\n            raise HTTPException(400, \"Invalid JSON\")\n\n        if not isinstance(body, dict):\n            raise HTTPException(400, \"Expected a JSON object\")\n\n        imported = []\n\n        # ── Memories ──\n        if \"memories\" in body and isinstance(body[\"memories\"], list):\n            # Strict load: importing on top of an unreadable store would write\n            # only the incoming rows back and drop everything already saved.\n            try:\n                existing = memory_manager.load_all_for_update()\n            except MemoryStoreUnreadable as e:\n                logger.error(\"Refusing to import memories: %s\", e)\n                raise HTTPException(\n                    503, \"Memory store is temporarily unreadable — nothing was imported.\"\n                )\n            # Dedup against THIS user's own memories only. Using every tenant's","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/backup_routes.py#L53-L89","documentation":"HTTP 400 raised by POST /api/import when request.json() throws — the request body is not valid JSON. FastAPI's Request.json() parses the raw body, so malformed JSON, wrong Content-Type payloads (form-encoded), or an empty body all land here. require_admin runs first, so this is always an authenticated caller sending a bad body.","triggerScenarios":"POST /api/import with truncated JSON, single-quoted keys, a trailing comma, an empty request body, or multipart/form-data containing the file instead of raw JSON.","commonSituations":"Uploading a manually edited export with a typo; using curl without --data-binary for a file; a proxy or client that mangles the body; exporting from an older version that produced invalid JSON.","solutions":["Validate the file parses as JSON before uploading: python -c \"import json,sys; json.load(open(sys.argv[1]))\" file.json.","Send the file raw with correct Content-Type: curl -H 'Content-Type: application/json' --data-binary @export.json.","If the export came from GET /api/export, re-export rather than hand-repairing."],"exampleFix":"# before\ncurl -X POST /api/import -F file=@export.json   # multipart -> Invalid JSON\n\n# after\ncurl -X POST /api/import -H 'Content-Type: application/json' --data-binary @export.json","handlingStrategy":"validation","validationCode":"import json\ndef load_export(path):\n    with open(path, encoding='utf-8') as f:\n        return json.load(f)  # raises here, before the upload\n# client-side:\ntry { JSON.parse(text); } catch { alert('File is not valid JSON'); }","typeGuard":null,"tryCatchPattern":"if (res.status === 400 && (await res.json()).detail === 'Invalid JSON') { showFileError('Not valid JSON'); return; }","preventionTips":["Always upload exports unmodified from /api/export.","Send raw body with Content-Type: application/json, never multipart.","JSON.parse-validate in the browser before uploading."],"tags":["validation","http-400","json","import","backup"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}