{"record":{"id":"54e5e79e4e9f4dc7","repo":"gildas-lormeau/SingleFile","slug":"unknown-token","errorCode":"unknown_token","errorMessage":"unknown_token","messagePattern":"unknown_token","errorType":"error_code","errorClass":"Error","httpStatus":400,"severity":"error","filePath":"src/lib/gdrive/gdrive.js","lineNumber":91,"sourceCode":"\t\t\t} else {\n\t\t\t\tdelete this.accessToken;\n\t\t\t\tdelete this.refreshToken;\n\t\t\t\tdelete this.expirationDate;\n\t\t\t}\n\t\t}\n\t}\n\tasync refreshAuthToken() {\n\t\tif (this.refreshToken) {\n\t\t\tconst httpResponse = await fetch(TOKEN_URL, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n\t\t\t\tbody: \"client_id=\" + this.clientId +\n\t\t\t\t\t\"&refresh_token=\" + this.refreshToken +\n\t\t\t\t\t\"&grant_type=refresh_token\" +\n\t\t\t\t\t\"&client_secret=\" + this.clientKey\n\t\t\t});\n\t\t\tif (httpResponse.status == 400) {\n\t\t\t\tthrow new Error(\"unknown_token\");\n\t\t\t}\n\t\t\tconst response = await getJSON(httpResponse);\n\t\t\tthis.accessToken = response.access_token;\n\t\t\tif (response.refresh_token) {\n\t\t\t\tthis.refreshToken = response.refresh_token;\n\t\t\t}\n\t\t\tif (response.expires_in) {\n\t\t\t\tthis.expirationDate = Date.now() + (response.expires_in * 1000);\n\t\t\t}\n\t\t\treturn { accessToken: this.accessToken, refreshToken: this.refreshToken, expirationDate: this.expirationDate };\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tif (browser.identity && browser.identity.removeCachedAuthToken && this.accessToken) {\n\t\t\t\t\tawait browser.identity.removeCachedAuthToken({ token: this.accessToken });\n\t\t\t\t}\n\t\t\t\tthis.accessToken = await getAuthToken({ interactive: false });\n\t\t\t\treturn { revokableAccessToken: this.accessToken };\n\t\t\t\t// eslint-disable-next-line no-unused-vars","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/gildas-lormeau/SingleFile/blob/517fb7c5cf2096d89933b747e862d8ecf616a9f9/src/lib/gdrive/gdrive.js#L73-L109","documentation":"gdrive's refreshAuthToken exchanges the stored refresh token with Google's OAuth token endpoint. A 400 response means Google rejected the refresh grant — typically an invalid, expired, or revoked refresh token — so the library throws 'unknown_token'.","triggerScenarios":"Calling auth/upload with a refresh token that has been revoked, expired (unused for 6+ months), invalidated by changing the Google account password, or issued for a different client_id/client_secret pair.","commonSituations":"Users revoking app access in Google Account settings; Google auto-expiring dormant refresh tokens; dev/client_secret mismatch after switching OAuth clients; multiple authorizations invalidating older tokens on unverified apps.","solutions":["Re-run the interactive OAuth flow to obtain a fresh refresh token (delete stored credentials first).","Verify clientId/clientKey (secret) match the OAuth client that issued the refresh token.","Ask the user to re-authorize the app in Google Account permissions if it was revoked.","Catch this error in upload code and fall back to launching a new auth flow instead of retrying the same token."],"exampleFix":"// before\nawait gdrive.auth({ refreshToken: storedToken }); // throws unknown_token\n// after\ntry {\n  await gdrive.auth({ refreshToken: storedToken });\n} catch (e) {\n  if (e.message === 'unknown_token') {\n    await gdrive.auth({ interactive: true }); // new OAuth flow\n  } else throw e;\n}","handlingStrategy":"try-catch","validationCode":"// verify a refresh token exists and matches current client credentials before auth\nif (!refreshToken || clientId !== CLIENT_ID_USED_AT_ISSUANCE || clientKey !== CLIENT_SECRET) {\n  await gdrive.auth({ interactive: true });\n}","typeGuard":null,"tryCatchPattern":"try {\n  await gdrive.auth({ refreshToken });\n} catch (e) {\n  if (e.message === 'unknown_token') {\n    await gdrive.auth({ interactive: true }); // re-run OAuth to get a new refresh token\n  } else throw e;\n}","preventionTips":["Never retry indefinitely with the same rejected refresh token","Detect client_id/client_secret changes after switching OAuth clients","Inform users when re-authorization is needed (password change, revoked access)"],"tags":["gdrive","oauth","token-refresh"],"backgroundTag":"oauth-token-revoked","analyzedSha":"517fb7c5cf2096d89933b747e862d8ecf616a9f9","analyzedAt":"2026-09-01T10:05:25.770Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}