{"record":{"id":"2f90bdfe80c68ce7","repo":"Significant-Gravitas/AutoGPT","slug":"only-oauth2-credentials-can-be-upgraded","errorCode":null,"errorMessage":"Only OAuth2 credentials can be upgraded","messagePattern":"Only OAuth2 credentials can be upgraded","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"warning","filePath":"autogpt_platform/backend/backend/api/features/integrations/router.py","lineNumber":980,"sourceCode":"\n    Raises HTTPException on validation failure.\n    \"\"\"\n    # Platform-owned system credentials must never be upgraded — scope\n    # changes here would leak across every user that shares them.\n    if is_system_credential(credential_id):\n        raise HTTPException(\n            status_code=status.HTTP_400_BAD_REQUEST,\n            detail=\"System credentials cannot be upgraded\",\n        )\n\n    existing = await creds_manager.store.get_creds_by_id(user_id, credential_id)\n    if not existing:\n        raise HTTPException(\n            status_code=status.HTTP_404_NOT_FOUND,\n            detail=\"Credential to upgrade not found\",\n        )\n    if not isinstance(existing, OAuth2Credentials):\n        raise HTTPException(\n            status_code=status.HTTP_400_BAD_REQUEST,\n            detail=\"Only OAuth2 credentials can be upgraded\",\n        )\n    if not provider_matches(existing.provider, provider.value):\n        raise HTTPException(\n            status_code=status.HTTP_400_BAD_REQUEST,\n            detail=\"Credential provider does not match the requested provider\",\n        )\n    if existing.is_managed:\n        raise HTTPException(\n            status_code=status.HTTP_400_BAD_REQUEST,\n            detail=\"Managed credentials cannot be upgraded\",\n        )\n\n    # Google handles scope merging via include_granted_scopes; others need\n    # the union of existing + new scopes in the login URL.\n    if provider != ProviderName.GOOGLE:\n        requested_scopes = list(set(requested_scopes) | set(existing.scopes))","sourceCodeStart":962,"sourceCodeEnd":998,"githubUrl":"https://github.com/Significant-Gravitas/AutoGPT/blob/9c8bb5550f446ba5d3046b78896578742495b3cf/autogpt_platform/backend/backend/api/features/integrations/router.py#L962-L998","documentation":"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.","triggerScenarios":"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.","commonSituations":"Providers offering multiple credential types where the UI shows one upgrade button for all connections; scripts assuming all credentials for a provider are OAuth.","solutions":["Check the credential type before offering upgrade — only credentials with type 'oauth2' are eligible.","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.","In the UI, hide/disable 'upgrade scopes' actions for non-OAuth credential types."],"exampleFix":"# before: offering upgrade for any credential\nif cred.provider == provider:\n    show_upgrade_button(cred)\n\n# after: only OAuth2 credentials\nif cred.provider == provider and cred.type == \"oauth2\":\n    show_upgrade_button(cred)","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"def is_oauth2(cred: dict) -> bool:\n    return cred.get(\"type\") == \"oauth2\"","tryCatchPattern":null,"preventionTips":["Gate 'upgrade scopes' UI on credential type == oauth2.","For API-key credentials, plan for re-issuing the key instead of scope upgrade."],"tags":["oauth","credentials","http-400","credential-type"],"backgroundTag":null,"analyzedSha":"9c8bb5550f446ba5d3046b78896578742495b3cf","analyzedAt":"2026-08-14T17:17:21.957Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}