transloadit/uppy · error · Error
OAuth2 error: ${response.error}
Error message
OAuth2 error: ${response.error} What it means
Thrown by the Google Picker's internal authorize() when the OAuth2 token flow completes but the response carries an error field (e.g. 'access_denied', 'popup_closed_by_user') instead of an access_token. It means Google refused or aborted issuance of the access token needed for the Picker API.
Source
Thrown at packages/@uppy/core/src/companion-client/googlePicker.ts:198
// Authorization scopes required by the API; multiple scopes can be included, separated by spaces.
scope: scopes.join(' '),
callback: resolve,
error_callback: reject,
})
if (accessToken === null) {
// Prompt the user to select a Google Account and ask for consent to share their data
// when establishing a new session.
tokenClient.requestAccessToken({ prompt: 'consent' })
} else {
// Skip display of account chooser and consent dialog for an existing session.
tokenClient.requestAccessToken({ prompt: '' })
}
},
)
if (response.error) {
throw new Error(`OAuth2 error: ${response.error}`)
}
return response.access_token
}
async function doLogout(accessToken: string): Promise<void> {
await new Promise<void>((resolve) =>
google.accounts.oauth2.revoke(accessToken, resolve),
)
}
export class InvalidTokenError extends Error {
constructor() {
super('Invalid or expired token')
this.name = 'InvalidTokenError'
}
}
async function handleDocObjectRecursively({View on GitHub (pinned to 5d4dedd02a)
Solutions
- Ensure the OAuth client's authorized JavaScript origins include your page origin
- Enable the Google Picker API (and Photos Picker API if used) and add required scopes in the plugin's pick options / client config
- Handle user cancellation gracefully — catch the error and keep the picker button in a signed-out state
- Add your test users if the OAuth consent screen is in testing mode
Example fix
// before
renderButton(uppy, { clientId: 'xxx' }) // origin not whitelisted -> OAuth2 error: access_denied
// after
renderButton(uppy, {
clientId: 'xxx',
// in Google Cloud Console: add https://app.example.com to Authorized JavaScript origins
// enable 'Google Picker API' + scopes: photospicker, drive.readonly
}) Defensive patterns
Strategy: try-catch
Validate before calling
null
Type guard
function isOAuth2Error(e: unknown): boolean {
return e instanceof Error && e.message.startsWith('OAuth2 error:')
} Try / catch
try {
await uppy.googlePicker.showPicker()
} catch (err) {
if (isOAuth2Error(err)) {
if (err.message.includes('access_denied')) return // user cancelled — ignore
uppy.info('Google sign-in failed. Check app configuration.', 'error', 5000)
}
} Prevention
- Whitelist all page origins in the OAuth client
- Enable Picker/Photos Picker APIs and add scopes before shipping
- Treat popup_closed/access_denied as non-errors
When it happens
Trigger: Calling uppy.googlePicker.showPicker() / renderButton() where the google Picker client's requestAccessToken callback receives response.error — user denies consent, closes the popup, or the OAuth client lacks the required scopes (e.g. missing Picker or Photos Picker scope).
Common situations: OAuth client ID misconfigured (unauthorized origin, missing scope 'https://www.googleapis.com/auth/photospicker'); user cancels the consent popup; Picker API not enabled in the Google Cloud project; app in testing mode with the test user not added.
Related errors
- Failed to list folder contents for '${doc.name}' (${doc.id})
- Failed to create a session
- Failed to get a media items
- Failed to get session
- Invalid request body
AI-assisted analysis of transloadit/uppy@5d4dedd02a (2026-08-28).
Data as JSON: /api/errors/9e6a3c01709ac2ee.
Report an issue: GitHub.