{"record":{"id":"c93429966b0e2f57","repo":"odysseus-dev/odysseus","slug":"your-account-is-not-allowed-to-use-model-sess-mo","errorCode":null,"errorMessage":"Your account is not allowed to use model '{sess.model}'.","messagePattern":"Your account is not allowed to use model '(.+?)'\\.","errorType":"http","errorClass":"HTTPException","httpStatus":403,"severity":"error","filePath":"routes/chat_helpers.py","lineNumber":214,"sourceCode":"    \"\"\"\n    try:\n        user = effective_user(request)\n    except Exception:\n        user = None\n    if not user:\n        return\n    auth_manager = getattr(getattr(request.app, \"state\", None), \"auth_manager\", None)\n    if not auth_manager:\n        return\n\n    privs = auth_manager.get_privileges(user) or {}\n\n    # Explicit \"block everything\" sentinel takes precedence over the\n    # allowlist — it's the only way to distinguish \"user clicked [None]\"\n    # (block all) from \"user clicked [All]\" (no restriction), since both\n    # otherwise produce an empty `allowed_models` list.\n    if privs.get(\"block_all_models\"):\n        raise HTTPException(403, f\"Your account is not allowed to use model '{sess.model}'.\")\n\n    allowed_models = _allowed_models_from_privileges(privs)\n    if allowed_models is not None and sess.model and sess.model not in allowed_models:\n        raise HTTPException(403, f\"Your account is not allowed to use model '{sess.model}'.\")\n\n    cap = int(privs.get(\"max_messages_per_day\") or 0)\n    if cap <= 0:\n        return\n\n    from datetime import datetime as _dt, timedelta as _td\n    from core.database import Session as _DbSess, ChatMessage as _Cm\n    db = SessionLocal()\n    try:\n        count = (\n            db.query(_Cm)\n            .join(_DbSess, _Cm.session_id == _DbSess.id)\n            .filter(_DbSess.owner == user,\n                    _Cm.role == \"user\",","sourceCodeStart":196,"sourceCodeEnd":232,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/chat_helpers.py#L196-L232","documentation":"Raised by the chat privilege gate when the authenticated user's privileges contain the 'block_all_models' sentinel. This sentinel is the explicit 'block everything' setting an admin sets when a user selected [None] in the model allowlist UI; it takes precedence over any allowlist interpretation so an empty list is not mistaken for 'no restriction'.","triggerScenarios":"Any chat send (streaming or non-streaming) where the user's privilege blob has block_all_models truthy. sess.model is echoed in the message but the block applies regardless of which model the session holds.","commonSituations":"Admin set the user's model access to [None] in the admin panel; a privilege record was written with the sentinel by a management script; the user still has an old chat session open and keeps hitting send.","solutions":["Ask an admin to clear the block-all setting or grant at least one allowed model for this user","If you are the admin, edit the user's privileges and set an explicit allowed_models list instead of [None]","Check the auth_manager privilege store for a stale block_all_models flag left by a prior configuration"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const me = await fetch('/api/me/privileges').then(r=>r.json());\nif (me.block_all_models) { disableChatCompose('Model access blocked for this account'); }","typeGuard":null,"tryCatchPattern":"try { await sendChat(...); } catch (e) { if (e.status === 403 && /not allowed to use model/.test(e.message)) { showModelLockedUI(); } else { throw e; } }","preventionTips":["Surface model-blocked state in the UI before the user types","Admins: prefer explicit allowlists over the [None]/block-all sentinel for partial access"],"tags":["authorization","privileges","fastapi","chat"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}