odysseus-dev/odysseus · error · HTTPException

Invalid JSON

Error message

Invalid JSON

What it means

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.

Source

Thrown at routes/backup_routes.py:71

            "preferences": preferences,
        }

        filename = f"odysseus_backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
        return Response(
            content=json.dumps(export_data, indent=2, ensure_ascii=False),
            media_type="application/json",
            headers={"Content-Disposition": f"attachment; filename={filename}"},
        )

    @router.post("/api/import")
    async def import_data(request: Request):
        """Import user data from a previously exported JSON file. Merges with existing data."""
        require_admin(request)
        user = get_current_user(request)
        try:
            body = await request.json()
        except Exception:
            raise HTTPException(400, "Invalid JSON")

        if not isinstance(body, dict):
            raise HTTPException(400, "Expected a JSON object")

        imported = []

        # ── Memories ──
        if "memories" in body and isinstance(body["memories"], list):
            # Strict load: importing on top of an unreadable store would write
            # only the incoming rows back and drop everything already saved.
            try:
                existing = memory_manager.load_all_for_update()
            except MemoryStoreUnreadable as e:
                logger.error("Refusing to import memories: %s", e)
                raise HTTPException(
                    503, "Memory store is temporarily unreadable — nothing was imported."
                )
            # Dedup against THIS user's own memories only. Using every tenant's

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Validate the file parses as JSON before uploading: python -c "import json,sys; json.load(open(sys.argv[1]))" file.json.
  2. Send the file raw with correct Content-Type: curl -H 'Content-Type: application/json' --data-binary @export.json.
  3. If the export came from GET /api/export, re-export rather than hand-repairing.

Example fix

# before
curl -X POST /api/import -F file=@export.json   # multipart -> Invalid JSON

# after
curl -X POST /api/import -H 'Content-Type: application/json' --data-binary @export.json
Defensive patterns

Strategy: validation

Validate before calling

import json
def load_export(path):
    with open(path, encoding='utf-8') as f:
        return json.load(f)  # raises here, before the upload
# client-side:
try { JSON.parse(text); } catch { alert('File is not valid JSON'); }

Try / catch

if (res.status === 400 && (await res.json()).detail === 'Invalid JSON') { showFileError('Not valid JSON'); return; }

Prevention

When it happens

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

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

Understand the failure class

Related errors


AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14). Data as JSON: /api/errors/d6cfce6ca49495d6. Report an issue: GitHub.