Significant-Gravitas/AutoGPT · warning · HTTPException

Only OAuth2 credentials can be upgraded

Error message

Only OAuth2 credentials can be upgraded

What it means

In _prepare_scope_upgrade, if the existing credential resolves but is not an OAuth2Credentials instance (e.g. it's an APIKeyCredentials or UserPasswordCredentials), HTTP 400 'Only OAuth2 credentials can be upgraded' is raised. Scope upgrade is an OAuth concept: it re-runs the authorization flow requesting more scopes, which has no meaning for API-key or user/password credentials.

Source

Thrown at autogpt_platform/backend/backend/api/features/integrations/router.py:980

    Raises HTTPException on validation failure.
    """
    # Platform-owned system credentials must never be upgraded — scope
    # changes here would leak across every user that shares them.
    if is_system_credential(credential_id):
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="System credentials cannot be upgraded",
        )

    existing = await creds_manager.store.get_creds_by_id(user_id, credential_id)
    if not existing:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Credential to upgrade not found",
        )
    if not isinstance(existing, OAuth2Credentials):
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Only OAuth2 credentials can be upgraded",
        )
    if not provider_matches(existing.provider, provider.value):
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Credential provider does not match the requested provider",
        )
    if existing.is_managed:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Managed credentials cannot be upgraded",
        )

    # Google handles scope merging via include_granted_scopes; others need
    # the union of existing + new scopes in the login URL.
    if provider != ProviderName.GOOGLE:
        requested_scopes = list(set(requested_scopes) | set(existing.scopes))

View on GitHub (pinned to 9c8bb5550f)

Solutions

  1. Check the credential type before offering upgrade — only credentials with type 'oauth2' are eligible.
  2. For API-key credentials, request additional access by getting a new key from the provider with the needed permissions and updating the stored secret — there is no scope-upgrade flow.
  3. In the UI, hide/disable 'upgrade scopes' actions for non-OAuth credential types.

Example fix

# before: offering upgrade for any credential
if cred.provider == provider:
    show_upgrade_button(cred)

# after: only OAuth2 credentials
if cred.provider == provider and cred.type == "oauth2":
    show_upgrade_button(cred)
Defensive patterns

Strategy: type-guard

Type guard

def is_oauth2(cred: dict) -> bool:
    return cred.get("type") == "oauth2"

Prevention

When it happens

Trigger: User selects a provider that supports both API-key and OAuth (e.g. a provider with a simple API key connection) and initiates 'upgrade scopes' against the API-key credential; request built with a credential_id that happens to belong to an API-key entry.

Common situations: Providers offering multiple credential types where the UI shows one upgrade button for all connections; scripts assuming all credentials for a provider are OAuth.

Related errors


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