odysseus-dev/odysseus · error · HTTPException

Memory store is temporarily unreadable — nothing was importe

Error message

Memory store is temporarily unreadable — nothing was imported.

What it means

HTTP 503 raised by POST /api/import when memory_manager.load_all_for_update() raises MemoryStoreUnreadable. The import path deliberately refuses to merge into an unreadable store: a naive merge would write only the incoming rows and silently drop all existing memories. 503 signals a transient server-side store problem — nothing was imported.

Source

Thrown at routes/backup_routes.py:86

        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
            # rows (load_all) meant a memory whose text matched any other
            # user's was silently skipped, so the importing user lost their own
            # data. The full store is still saved back below.
            existing_texts = {e.get("text", "").strip().lower()
                              for e in existing if e.get("owner") == user}
            added = 0
            for mem in body["memories"]:
                if not isinstance(mem, dict) or not mem.get("text"):
                    continue
                if mem["text"].strip().lower() in existing_texts:
                    continue  # skip duplicates
                # Assign owner when auth is enabled
                if user and not mem.get("owner"):
                    mem["owner"] = user
                existing.append(mem)

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Retry the import after the store becomes readable (check the server log line 'Refusing to import memories').
  2. Resolve the underlying store issue: lock contention, file permissions, or corruption repair.
  3. Keep the export file — nothing was merged, so a clean retry is safe.
  4. Do not attempt to 'force' the import by clearing the store; that is exactly the data-loss path the guard prevents.
Defensive patterns

Strategy: retry

Try / catch

catch (e) {
  if (e.status === 503) { scheduleRetry(seconds(30)); /* keep export file, nothing was merged */ }
  else throw e;
}

Prevention

When it happens

Trigger: POST /api/import (with a 'memories' list) while the memory store file/database is locked, corrupt, or otherwise unreadable (MemoryStoreUnreadable). Concurrent write, disk issue, or a partially-written store file.

Common situations: Importing while another process holds the store; store file corrupted after a crash mid-write; permissions changed on the data directory; importing immediately after an unclean shutdown.

Related errors


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