odysseus-dev/odysseus · error · ValueError
ChatGPT token response was missing access_token or refresh_t
Error message
ChatGPT token response was missing access_token or refresh_token
What it means
A server-side ValueError raised in _provision_endpoint when the ChatGPT OAuth device-flow token exchange returns a payload without access_token or refresh_token. It aborts endpoint provisioning before any DB record is written; the caller of the route typically surfaces it as a 500/4xx depending on outer handling. It indicates the OAuth exchange with the ChatGPT/Codex issuer succeeded HTTP-wise but returned an incomplete or unexpected token payload.
Source
Thrown at routes/chatgpt_subscription_routes.py:29
from routes.device_flow import (
DeviceFlowPoll,
DeviceFlowStart,
PendingDeviceFlowStore,
create_device_flow_router,
)
from src.auth_helpers import get_current_user
from src import chatgpt_subscription
logger = logging.getLogger(__name__)
_DEVICE_FLOW_STORE = PendingDeviceFlowStore()
def _provision_endpoint(tokens: Dict, owner: Optional[str]) -> Dict:
access_token = tokens.get("access_token")
refresh_token = tokens.get("refresh_token")
if not access_token or not refresh_token:
raise ValueError("ChatGPT token response was missing access_token or refresh_token")
base = chatgpt_subscription.DEFAULT_CHATGPT_SUBSCRIPTION_BASE_URL
models = chatgpt_subscription.fetch_available_models(access_token)
if not models:
raise ValueError("ChatGPT Subscription connected, but no usable Codex models were discovered for this account.")
db = SessionLocal()
try:
auth = (
db.query(ProviderAuthSession)
.filter(
ProviderAuthSession.provider == chatgpt_subscription.CHATGPT_SUBSCRIPTION_PROVIDER,
ProviderAuthSession.owner == owner,
)
.first()
)
if auth is None:
auth = ProviderAuthSession(
id=str(uuid.uuid4())[:8],View on GitHub (pinned to f9235ebbf1)
Solutions
- Restart the device flow from the beginning (request a new device code) instead of retrying the exchange — device codes are single-use.
- Check server logs for the raw token response shape from chatgpt_subscription's exchange call to confirm which field is missing.
- Update to the latest version of the app/chatgpt_subscription module in case the issuer response format changed.
- If behind a proxy, bypass it for the ChatGPT OAuth issuer domain and retry.
Defensive patterns
Strategy: retry
Type guard
def has_token_fields(tokens: dict) -> bool:
return bool(tokens.get('access_token')) and bool(tokens.get('refresh_token')) Try / catch
# server-side caller of _provision_endpoint
try:
_provision_endpoint(tokens, owner)
except ValueError as e:
if 'missing access_token or refresh_token' in str(e):
_DEVICE_FLOW_STORE.clear() # stale/used device code; restart flow
raise HTTPException(502, 'Token exchange incomplete, restart the connection flow')
raise Prevention
- Never re-submit a used device code — always begin a fresh device flow after any exchange failure.
- Log (safe, non-secret) key names of the token response at debug level to detect issuer schema drift early.
- Pin a known-good version of the OAuth client and test after issuer-side API changes.
When it happens
Trigger: Completing ChatGPT Subscription device-code login where the token endpoint response is missing fields (issuer API change), the response was an error envelope parsed as a token dict, a proxy/interceptor mangled the JSON, or the code exchanged a device code that had already been consumed/expired.
Common situations: OpenAI changes the ChatGPT OAuth response shape; clock skew or expired device_code producing an error body that still parses as a dict; retrying a device flow after the user refreshed the page and the code was already redeemed; corporate MITM proxy stripping response fields.
Related errors
- ChatGPT did not return a complete device code
- Request failed (HTTP ${response.status})
- Unknown device-flow provider: ${provider}
- ${cfg.label} sign-in did not return a poll id
- GitHub device-code request failed (HTTP {status})
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/861b289b44242c22.
Report an issue: GitHub.