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

  1. 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)
  2. Verify COMPANION_GOOGLE_KEY and COMPANION_GOOGLE_SECRET match the current Google OAuth client
  3. Log the full token response body (excluding secrets) to confirm the OAuth error Google returns
  4. 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

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


AI-assisted analysis of transloadit/uppy@5d4dedd02a (2026-08-28). Data as JSON: /api/errors/949ba5a73a87c7fd. Report an issue: GitHub.