Significant-Gravitas/AutoGPT · warning · HTTPException

Managed credentials cannot be upgraded

Error message

Managed credentials cannot be upgraded

What it means

In _prepare_scope_upgrade, if the existing OAuth2 credential has is_managed=True (provisioned and governed by an external identity/secret manager rather than the user), HTTP 400 'Managed credentials cannot be upgraded' is raised. Managed credentials are lifecycle-controlled by the managing system; user-initiated scope merges could desync the manager's state or grant scopes the manager did not approve.

Source

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

    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))

    return requested_scopes


async def _merge_or_create_credential(
    user_id: str,
    provider: ProviderName,
    credentials: OAuth2Credentials,
    credential_id: str | None,
) -> OAuth2Credentials:

View on GitHub (pinned to 9c8bb5550f)

Solutions

  1. Exclude is_managed credentials from upgrade actions in the UI/API calls.
  2. To get more scopes, create a personal OAuth connection for the provider instead of touching the managed one.
  3. If the managed credential genuinely needs new scopes, update it through the managing system (the secret manager / provisioning pipeline), not the user upgrade API.

Example fix

# before
eligible = [c for c in creds if c.provider == provider]

# after
eligible = [c for c in creds if c.provider == provider and not c.is_managed]
Defensive patterns

Strategy: type-guard

Type guard

def can_upgrade(cred: dict) -> bool:
    return cred["type"] == "oauth2" and not cred.get("is_managed") and not is_system_credential(cred["id"])

Prevention

When it happens

Trigger: Initiating scope upgrade on a credential flagged is_managed — e.g. enterprise-deployed credentials distributed to users via a secret manager integration; attempting to upgrade via the login URL with upgrade_credentials_id pointing at a managed credential.

Common situations: Enterprise/shared deployments where some connections are provisioned centrally; UI not distinguishing managed vs personal credentials in the upgrade flow.

Related errors


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