odysseus-dev/odysseus · error · HTTPException
API token missing required scope: {' and '.join(sorted(missi
Error message
API token missing required scope: {' and '.join(sorted(missing))} What it means
Raised as HTTP 403 by _scope_owner_all when an API-token caller is missing one or more of a route's required scopes — this variant demands ALL scopes (set difference), and the message interpolates the missing ones joined by 'and'. It appears on routes combining multiple integrations, such as POST /api/codex/emails/draft-document which needs both email-draft and documents-write scopes.
Source
Thrown at routes/codex_routes.py:105
if getattr(request.state, "api_token", False):
scopes = set(getattr(request.state, "api_token_scopes", []) or [])
if not scopes.intersection(allowed):
required = " or ".join(sorted(allowed))
raise HTTPException(403, f"API token missing required scope: {required}")
owner = getattr(request.state, "api_token_owner", None)
if not owner:
raise HTTPException(403, "API token has no owner")
return owner
return require_user(request)
def _scope_owner_all(request: Request, required: set[str]) -> str:
"""Return owner only when an API token has every required scope."""
if getattr(request.state, "api_token", False):
scopes = set(getattr(request.state, "api_token_scopes", []) or [])
missing = required - scopes
if missing:
raise HTTPException(403, f"API token missing required scope: {' and '.join(sorted(missing))}")
owner = getattr(request.state, "api_token_owner", None)
if not owner:
raise HTTPException(403, "API token has no owner")
return owner
return require_user(request)
def _require_cookbook_scope(request: Request, allowed: set[str]) -> str:
"""Authorize a Codex cookbook route.
For API-token callers, enforce the given scope set.
For cookie-session callers, additionally require admin privileges
because cookbook surfaces expose host topology, task logs, tmux
commands, and model-serving controls.
"""
owner = _scope_owner(request, allowed)
if not getattr(request.state, "api_token", False):
require_admin(request)View on GitHub (pinned to f9235ebbf1)
Solutions
- Parse the missing list from the message and re-issue the token adding exactly those scopes.
- Verify with the token introspection endpoint that all required scopes are present before calling combined routes.
- Keep tokens per-purpose: mint a dedicated token with the full scope set for cross-integration automation rather than reusing a narrow one.
Example fix
# before curl -H 'Authorization: Bearer $EMAIL_ONLY_TOKEN' -X POST .../api/codex/emails/draft-document # 403 # after TOKEN=$(create_token --scope email:write --scope documents:write) curl -H "Authorization: Bearer $TOKEN" -X POST .../api/codex/emails/draft-document
Defensive patterns
Strategy: validation
Validate before calling
const REQUIRED = ['email:write', 'documents:write']; // draft-document route
const {scopes} = await introspect(token);
const missing = REQUIRED.filter(s => !scopes.includes(s));
if (missing.length) await reissueToken([...scopes, ...missing]); Type guard
function hasAllScopes(have: string[], need: string[]): boolean {
return need.every(s => have.includes(s));
} Try / catch
try { r = await draftDocument() } catch (e) { if (e.status === 403 && e.detail.includes('missing required scope')) { reissue with missing scopes; retry once } else throw } Prevention
- For multi-integration routes, provision one token carrying the union of all required scopes.
- On app upgrades, diff route scope requirements and re-issue tokens before deploying clients.
- Parse 403 detail messages programmatically to extract the exact missing scope list.
When it happens
Trigger: Calling a multi-scope route (e.g. codex_email_draft_document) with a token that has email:write but not the DOCS_WRITE_SCOPES entries; a token provisioned before a route gained an additional required scope.
Common situations: Route's scope requirements expanded in a new release so previously-working tokens now 403; token minted from a scope template that omits one integration.
Related errors
- API token missing required scope: {required}
- API token requires chat scope
- API token owner mismatch
- API token is not scoped for chat
- Admin only
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/cfdb28674ddd9edb.
Report an issue: GitHub.