odysseus-dev/odysseus · error · HTTPException

Assistant not available

Error message

Assistant not available

What it means

Raised by GET /api/assistant/settings when _get_or_create(owner) returns None. That helper looks up the CrewMember flagged is_default_assistant for the owner, and if absent calls task_scheduler.ensure_assistant_defaults then re-queries; None means both the lookup and the post-seed re-query found nothing, i.e. the lazy seed failed without raising.

Source

Thrown at routes/assistant_routes.py:141

    async def get_assistant_session(request: Request):
        """Resolve (or lazily create) the pinned Assistant session for this user."""
        owner = _owner(request)
        crew = await _get_or_create(owner)
        if not crew or not crew.session_id:
            raise HTTPException(status_code=500, detail="Assistant session could not be resolved")
        return {
            "session_id": crew.session_id,
            "crew_member_id": crew.id,
            "name": crew.name,
        }

    @router.get("/settings")
    async def get_assistant_settings(request: Request):
        """Return CrewMember fields + the three check-in task rows + task IDs for logs."""
        owner = _owner(request)
        crew = await _get_or_create(owner)
        if not crew:
            raise HTTPException(status_code=500, detail="Assistant not available")
        db = SessionLocal()
        try:
            tasks = db.query(ScheduledTask).filter(
                ScheduledTask.owner == owner,
                ScheduledTask.crew_member_id == crew.id,
            ).order_by(ScheduledTask.scheduled_time.asc()).all()
            return {
                "crew": _crew_to_dict(crew),
                "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)

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Retry the request once (lazy seeding is idempotent) — transient DB locks during first seed are the most common cause.
  2. Check server logs for exceptions from task_scheduler.ensure_assistant_defaults for this owner.
  3. Verify the crew_members table exists and has the is_default_assistant column populated (schema current after upgrades).
  4. If the row keeps disappearing, look for a concurrent cleanup/admin-wipe job deleting it between seed and read.
Defensive patterns

Strategy: retry

Validate before calling

const s = await fetch('/api/auth/status', {credentials: 'include'});
if (!s.ok) throw new Error('not authenticated'); // fix auth before expecting assistant data

Try / catch

try {
  const r = await fetch('/api/assistant/settings', {credentials: 'include'});
  if (r.status === 500 && (await r.json()).detail === 'Assistant not available') {
    await new Promise(res => setTimeout(res, 1500));
    return (await fetch('/api/assistant/settings', {credentials: 'include'})).json();
  }
  if (!r.ok) throw new Error(`assistant settings failed: ${r.status}`);
  return r.json();
} catch (e) { throw e; }

Prevention

When it happens

Trigger: First authenticated GET /api/assistant/settings for a user when ensure_assistant_defaults raises internally (DB locked/corrupt) or returns without committing the CrewMember row; the crew row was deleted (admin wipe) and reseeding is broken; RESERVED_USERNAMES check passed but the seed path is short-circuited.

Common situations: Admin-wipe or user-deletion flows that removed crew_members rows while the app's seeding code path had an error; SQLite 'database is locked' during the seed transaction; restored/partial database missing the crew_members table contents.

Related errors


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