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
- Have the client re-GET /api/assistant/settings (re-seeds the assistant) and re-apply the PATCH.
- Check whether an admin wipe / user deletion ran concurrently; if so, re-authenticate and reload settings first.
- Verify all worker processes share the same database file (single SQLite path, no per-process copies).
- 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
- Don't keep settings pages open for hours before saving — re-GET immediately before PATCH.
- Serialize admin-wipe/user-deletion with in-flight settings writes.
- Run a single app process per database file to avoid split-brain deletes.
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
- Assistant session could not be resolved
- Assistant not available
- Failed to list calendars
- {key} must be an integer
- Failed to delete calendar
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/5466b7207c3e7b30.
Report an issue: GitHub.