{"record":{"id":"a45437b02d8dfcd7","repo":"odysseus-dev/odysseus","slug":"assistant-not-available","errorCode":null,"errorMessage":"Assistant not available","messagePattern":"Assistant not available","errorType":"http","errorClass":"HTTPException","httpStatus":500,"severity":"error","filePath":"routes/assistant_routes.py","lineNumber":141,"sourceCode":"    async def get_assistant_session(request: Request):\n        \"\"\"Resolve (or lazily create) the pinned Assistant session for this user.\"\"\"\n        owner = _owner(request)\n        crew = await _get_or_create(owner)\n        if not crew or not crew.session_id:\n            raise HTTPException(status_code=500, detail=\"Assistant session could not be resolved\")\n        return {\n            \"session_id\": crew.session_id,\n            \"crew_member_id\": crew.id,\n            \"name\": crew.name,\n        }\n\n    @router.get(\"/settings\")\n    async def get_assistant_settings(request: Request):\n        \"\"\"Return CrewMember fields + the three check-in task rows + task IDs for logs.\"\"\"\n        owner = _owner(request)\n        crew = await _get_or_create(owner)\n        if not crew:\n            raise HTTPException(status_code=500, detail=\"Assistant not available\")\n        db = SessionLocal()\n        try:\n            tasks = db.query(ScheduledTask).filter(\n                ScheduledTask.owner == owner,\n                ScheduledTask.crew_member_id == crew.id,\n            ).order_by(ScheduledTask.scheduled_time.asc()).all()\n            return {\n                \"crew\": _crew_to_dict(crew),\n                \"check_ins\": [_task_to_checkin_dict(t) for t in tasks],\n                \"task_ids\": [t.id for t in tasks],\n            }\n        finally:\n            db.close()\n\n    @router.patch(\"/settings\")\n    async def update_assistant_settings(payload: AssistantSettingsUpdate, request: Request):\n        \"\"\"Update CrewMember fields and/or check-in tasks in one call.\"\"\"\n        owner = _owner(request)","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/assistant_routes.py#L123-L159","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Retry the request once (lazy seeding is idempotent) — transient DB locks during first seed are the most common cause.","Check server logs for exceptions from task_scheduler.ensure_assistant_defaults for this owner.","Verify the crew_members table exists and has the is_default_assistant column populated (schema current after upgrades).","If the row keeps disappearing, look for a concurrent cleanup/admin-wipe job deleting it between seed and read."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"const s = await fetch('/api/auth/status', {credentials: 'include'});\nif (!s.ok) throw new Error('not authenticated'); // fix auth before expecting assistant data","typeGuard":null,"tryCatchPattern":"try {\n  const r = await fetch('/api/assistant/settings', {credentials: 'include'});\n  if (r.status === 500 && (await r.json()).detail === 'Assistant not available') {\n    await new Promise(res => setTimeout(res, 1500));\n    return (await fetch('/api/assistant/settings', {credentials: 'include'})).json();\n  }\n  if (!r.ok) throw new Error(`assistant settings failed: ${r.status}`);\n  return r.json();\n} catch (e) { throw e; }","preventionTips":["Hit GET /api/assistant/session once after login to force seeding before rendering settings.","Avoid admin-wipe or user-deletion jobs racing active user sessions.","Keep the SQLite file on a filesystem that supports locking; 'database is locked' during seed is the usual root cause."],"tags":["fastapi","database","assistant","seeding","sqlalchemy"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}