{"record":{"id":"7182f18449f74934","repo":"apache/superset","slug":"missing-oauth2-token","errorCode":null,"errorMessage":"Missing OAuth2 token","messagePattern":"Missing OAuth2 token","errorType":"exception","errorClass":"MissingOAuth2TokenError","httpStatus":500,"severity":"error","filePath":"superset/commands/database/sync_permissions.py","lineNumber":121,"sourceCode":"        # Need user info to impersonate for OAuth2 connections. The id is\n        # captured here, at validation/enqueue time, so that an async run of\n        # this command binds to whoever held the username right now, rather\n        # than re-resolving the (mutable) username at execution time.\n        if not self.username or not (\n            user := security_manager.get_user_by_username(self.username)\n        ):\n            raise UserNotFoundInSessionError()\n        self._user_id = user.id\n\n        with self.db_connection.get_sqla_engine() as engine:\n            try:\n                alive = ping(engine)\n            except Exception as err:\n                if (\n                    self.db_connection.is_oauth2_enabled()\n                    and self.db_connection.db_engine_spec.needs_oauth2(err)\n                ):\n                    raise MissingOAuth2TokenError() from err\n                raise DatabaseConnectionFailedError() from err\n\n        if not alive:\n            raise DatabaseConnectionFailedError()\n\n    def run(self) -> None:\n        \"\"\"\n        Triggers the perm sync in sync or async mode.\n        \"\"\"\n        self.validate()\n        if self.async_mode:\n            sync_database_permissions_task.delay(\n                self.db_connection_id, self._user_id, self.old_db_connection_name\n            )\n            return\n\n        self.sync_database_permissions()\n","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/apache/superset/blob/f4587218dd19d046c3e4d00063e7d27f8a2ed354/superset/commands/database/sync_permissions.py#L103-L139","documentation":"MissingOAuth2TokenError is raised in validate() when pinging the database engine raises, the database has OAuth2 enabled (is_oauth2_enabled()), and the engine spec classifies the error via needs_oauth2(err) as a missing/expired OAuth2 token. Superset uses OAuth2 for engines like Databricks and must re-authenticate before it can connect.","triggerScenarios":"Syncing permissions on an OAuth2-enabled database whose access token expired and no refresh token is stored; the OAuth2 token cache was cleared; the refresh flow was never completed for this user.","commonSituations":"Databricks (or similar) connections after token expiry; users who never went through the OAuth2 authorize flow; rotated client secrets invalidating refresh tokens.","solutions":["Re-authenticate via the OAuth2 flow (database connections UI -> authorize again) so a fresh token is issued","Check the OAuth2 client credentials in the database connection settings and the driver config","Inspect stored tokens/refresh errors in the logs for the underlying failure"],"exampleFix":"// before\n SyncPermissionsCommand(db_id, username).run()  # MissingOAuth2TokenError\n\n// after\n # complete the OAuth2 authorize flow in the UI first, then:\n SyncPermissionsCommand(db_id, username).run()","handlingStrategy":"retry","validationCode":"if db_conn.is_oauth2_enabled():\n    # ensure an access token exists before syncing\n    token = security_manager.get_oauth2_access_token(db_conn)\n    if token is None:\n        raise PermissionError(\"complete the OAuth2 authorize flow first\")","typeGuard":null,"tryCatchPattern":"try:\n    cmd.run()\nexcept MissingOAuth2TokenError:\n    # send user through the OAuth2 authorize flow, then retry once\n    ...","preventionTips":["Re-authorize OAuth2 connections on a schedule before expiry","Monitor token refresh failures in logs"],"tags":["oauth2","database","auth","network"],"backgroundTag":null,"analyzedSha":"f4587218dd19d046c3e4d00063e7d27f8a2ed354","analyzedAt":"2026-08-14T22:39:27.425Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}