Significant-Gravitas/AutoGPT · error · HTTPException

Invalid or expired state token

Error message

Invalid or expired state token

What it means

Raised (HTTP 400) by the external OAuth complete endpoint when `creds_manager.store.verify_state_token(user_id, state_token, provider)` returns None. Verification is a constant-time token comparison plus a provider match and an expiry check; a valid state is also single-use (it is removed on success), so replaying a completed flow fails the same way.

Source

Thrown at autogpt_platform/backend/backend/api/external/v1/integrations.py:419

    auth: APIAuthorizationInfo = Security(
        require_permission(APIKeyPermission.MANAGE_INTEGRATIONS)
    ),
) -> OAuthCompleteResponse:
    """
    Complete an OAuth flow by exchanging the authorization code for tokens.

    This endpoint should be called after the user has authorized the application
    and been redirected back to the external app's callback URL with an
    authorization code.
    """
    # Verify state token
    valid_state = await creds_manager.store.verify_state_token(
        auth.user_id, request.state_token, provider
    )

    if not valid_state:
        logger.warning(f"Invalid or expired state token for provider {provider}")
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Invalid or expired state token",
        )

    # Verify this is an external flow (callback_url must be set)
    if not valid_state.callback_url:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="State token was not created for external OAuth flow",
        )

    # Get OAuth handler with the original callback URL
    handler = _get_oauth_handler_for_external(provider, valid_state.callback_url)

    try:
        scopes = valid_state.scopes
        scopes = handler.handle_default_scopes(scopes)

View on GitHub (pinned to 9c8bb5550f)

Solutions

  1. Re-initiate the flow via the authorize endpoint to get a fresh state token and complete the callback promptly.
  2. Make the callback idempotent-safe on the client: never retry a used state; on 400 'Invalid or expired state token' always restart from authorize.
  3. Verify you pass the same provider in the URL as was used at initiation (provider must match `provider_matches`).
  4. Check the state token is transmitted unmodified (no trimming, re-encoding, or JSON escaping issues).

Example fix

# before: reusing an old/consumed state
POST /integrations/github/oauth/callback
{"state_token": "st_used_or_expired", "code": "..."}  # 400

# after: restart the flow
POST /integrations/github/oauth/authorize  -> new state_token
POST /integrations/github/oauth/callback {"state_token": new, "code": "..."}
Defensive patterns

Strategy: try-catch

Try / catch

try:
    result = client.post(f"/integrations/{provider}/oauth/callback", json={"state_token": state, "code": code})
except HTTPError as e:
    if e.response.status_code == 400 and "state token" in e.response.text.lower():
        # state is single-use and expiring: always restart the flow
        state = start_new_flow(provider)
        result = client.post(f"/integrations/{provider}/oauth/callback", json={"state_token": state, "code": new_code})
    else:
        raise

Prevention

When it happens

Trigger: POST `/api/external-api/v1/integrations/{provider}/oauth/callback` with a state_token that is expired (past `expires_at`), already consumed, issued for a different provider, issued for a different user, or corrupted/missing. Calling the callback endpoint twice with the same state is the classic replay case.

Common situations: User sits on the provider consent screen longer than the state TTL; double-submit of the callback (refresh or retry after a network error); state serialized incorrectly by the external app (truncation/encoding); mixing states between the internal platform OAuth flow and the external flow.

Related errors


AI-assisted analysis of Significant-Gravitas/AutoGPT@9c8bb5550f (2026-08-14). Data as JSON: /api/errors/f6c5cdeb757d6ee9. Report an issue: GitHub.