odysseus-dev/odysseus · error · HTTPException

Assistant not found

Error message

Assistant not found

What it means

Raised by PATCH /api/assistant/settings with 404 when the second, write-scoped query (db.query(CrewMember).filter(CrewMember.id == crew.id)) finds no row. The crew object was resolved moments earlier by _get_or_create, so this means the row vanished between the resolution read and the re-query inside the PATCH — a delete race, not a bad payload.

Source

Thrown at routes/assistant_routes.py:168

                "check_ins": [_task_to_checkin_dict(t) for t in tasks],
                "task_ids": [t.id for t in tasks],
            }
        finally:
            db.close()

    @router.patch("/settings")
    async def update_assistant_settings(payload: AssistantSettingsUpdate, request: Request):
        """Update CrewMember fields and/or check-in tasks in one call."""
        owner = _owner(request)
        crew = await _get_or_create(owner)
        if not crew:
            raise HTTPException(status_code=500, detail="Assistant not available")

        db = SessionLocal()
        try:
            crew_db = db.query(CrewMember).filter(CrewMember.id == crew.id).first()
            if not crew_db:
                raise HTTPException(status_code=404, detail="Assistant not found")

            # Update CrewMember fields.
            if payload.name is not None:
                crew_db.name = payload.name.strip() or crew_db.name
            if payload.avatar is not None:
                crew_db.avatar = payload.avatar
            if payload.personality is not None:
                crew_db.personality = payload.personality
            if payload.model is not None:
                crew_db.model = payload.model or None
            if payload.endpoint_url is not None:
                crew_db.endpoint_url = payload.endpoint_url or None
            if payload.timezone is not None:
                crew_db.timezone = payload.timezone or None

            # Tool list: either explicit list, or implicit toggle.
            if payload.enabled_tools is not None:
                crew_db.enabled_tools = json.dumps(payload.enabled_tools)

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Have the client re-GET /api/assistant/settings (re-seeds the assistant) and re-apply the PATCH.
  2. Check whether an admin wipe / user deletion ran concurrently; if so, re-authenticate and reload settings first.
  3. Verify all worker processes share the same database file (single SQLite path, no per-process copies).
  4. If reproducible, capture the row's existence right before the PATCH — a repeat failure indicates a deletion job, not a FastAPI bug.
Defensive patterns

Strategy: try-catch

Try / catch

try {
  const r = await fetch('/api/assistant/settings', {method: 'PATCH', ...});
  if (r.status === 404) {
    // row vanished mid-request: reseed via GET, then re-apply
    await fetch('/api/assistant/session', {credentials: 'include'});
    return fetch('/api/assistant/settings', {method: 'PATCH', ...});
  }
  return r;
} catch (e) { throw e; }

Prevention

When it happens

Trigger: An admin-wipe or user-deletion flow deletes the CrewMember between _get_or_create and the PATCH's re-query; the app was restarted against a different/blank database file between the two reads; a concurrent settings PATCH deleted and recreated the assistant row.

Common situations: User clicks Save in the settings UI at the same moment an admin wipe or account deletion runs; multi-process deployment where one process points at a different SQLite file; long-idle settings page whose underlying row was removed.

Related errors


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