transloadit/uppy · error · Error
Missing access_token
Error message
Missing access_token
What it means
After Google's OAuth token endpoint responds to a refresh request, the response body must contain a non-empty string access_token. If the field is missing, not a string, or empty, Companion throws 'Missing access_token' wrapped in Google error handling — almost always because the refresh token itself was revoked or expired and Google returned an error payload instead.
Source
Thrown at packages/@uppy/companion/src/server/provider/google/index.ts:39
}): Promise<{ accessToken: string }> {
return withGoogleErrorHandling(
'google',
'provider.google.token.refresh.error',
async () => {
const tokenRes = await getOauthClient()
.post('token', {
responseType: 'json',
form: {
refresh_token: theRefreshToken,
grant_type: 'refresh_token',
client_id: clientId,
client_secret: clientSecret,
},
})
.json<{ access_token?: unknown }>()
const accessToken = tokenRes.access_token
if (typeof accessToken !== 'string' || accessToken.length === 0) {
throw new Error('Missing access_token')
}
return { accessToken }
},
)
}
export async function logout({
providerUserSession: { accessToken: token },
}: {
providerUserSession: { accessToken: string }
}): Promise<{ revoked: true }> {
return withGoogleErrorHandling(
'google',
'provider.google.logout.error',
async () => {
await got.post('https://accounts.google.com/o/oauth2/revoke', {
searchParams: { token },
responseType: 'json',View on GitHub (pinned to 5d4dedd02a)
Solutions
- Have the user re-authenticate with Google so a fresh refresh token is stored (invalid_grant from a revoked/expired refresh token is the most common cause)
- Verify COMPANION_GOOGLE_KEY and COMPANION_GOOGLE_SECRET match the current Google OAuth client
- Log the full token response body (excluding secrets) to confirm the OAuth error Google returns
- Check that the stored providerUserSession still contains a valid refresh_token
Example fix
// before
const { accessToken } = await googleProvider.refreshToken({ refreshToken, companion })
// after
try {
const { accessToken } = await googleProvider.refreshToken({ refreshToken, companion })
} catch (err) {
// refresh token revoked/expired -> force re-auth
throw new ProviderAuthError(err, 401)
} Defensive patterns
Strategy: try-catch
Validate before calling
if (typeof refreshToken !== 'string' || refreshToken.length === 0) {
throw new ProviderAuthError('no refresh token', 401) // force re-login early
} Type guard
const hasRefreshToken = (s: unknown): s is { refreshToken: string } =>
typeof (s as { refreshToken?: unknown })?.refreshToken === 'string' && (s as { refreshToken: string }).refreshToken.length > 0 Try / catch
try { await provider.refreshToken(args) } catch (e) { if (e instanceof Error && e.message === 'Missing access_token') { await restartOAuthFlow() } throw e } Prevention
- Re-authenticate users proactively when refresh fails once instead of retrying
- Keep OAuth client id/secret in sync with the Google console
- Store refresh tokens securely and never reuse ones from revoked grants
When it happens
Trigger: Calling refreshToken() with a stale/revoked refresh token; Google responding with { error: 'invalid_grant' } (no access_token field); a malformed client secret causing a non-token response body; clock/environment issues that make Google reject the request.
Common situations: User revoked app access in their Google account; refresh token older than 6 months (Google expires them); COMPANION_GOOGLE_KEY/SECRET mismatch; OAuth client credentials rotated without updating Companion.
Related errors
- Unexpected OneDrive token refresh response
- Not implemented
- Missing access_token
- Missing S3 object key for aborting upload
- Missing S3 object key for resuming upload
AI-assisted analysis of transloadit/uppy@5d4dedd02a (2026-08-28).
Data as JSON: /api/errors/949ba5a73a87c7fd.
Report an issue: GitHub.