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'sView on GitHub (pinned to f9235ebbf1)
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.
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
- 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.
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
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Expected a JSON object
- await res.text()
- HTTP ${res.status}
- No contact data found
- {key} must be an integer
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/d6cfce6ca49495d6.
Report an issue: GitHub.