gildas-lormeau/SingleFile · error · Error
invalid_auth_response
invalid_auth_response
Error message
invalid_auth_response
What it means
After browser.identity.launchWebAuthFlow completes, initAuth verifies the OAuth 'state' query parameter on the redirect URL against the random state it generated. A mismatch means the redirect did not correspond to this auth request, so it throws 'invalid_auth_response' to prevent CSRF / response mix-ups.
Source
Thrown at src/lib/gdrive/gdrive.js:296
const response = await getJSON(httpResponse);
gdrive.accessToken = response.access_token;
gdrive.refreshToken = response.refresh_token;
gdrive.expirationDate = Date.now() + (response.expires_in * 1000);
return { accessToken: gdrive.accessToken, refreshToken: gdrive.refreshToken, expirationDate: gdrive.expirationDate };
}
async function initAuth(gdrive, options, state) {
let code;
const authFlow = { state };
try {
if (nativeWebAuthFlowSupported() && !options.forceWebAuthFlow) {
const authURL = await browser.identity.launchWebAuthFlow({
interactive: options.interactive,
url: gdrive.authURL
});
const searchParams = new URLSearchParams(new URL(authURL).search);
if (searchParams.get("state") != state) {
throw new Error("invalid_auth_response");
}
options.code = searchParams.get("code");
return await authFromCode(gdrive, options);
} else if (options.launchWebAuthFlow) {
options.extractAuthCode(browser.identity.getRedirectURL(), authFlow)
.then(authCode => code = authCode)
.catch(() => { /* ignored */ });
return await options.launchWebAuthFlow({ url: gdrive.authURL }, authFlow);
} else {
throw new Error("auth_not_supported");
}
}
catch (error) {
if (error.message && (error.message == "code_required" || error.message.includes("access"))) {
if (code) {
options.code = code;
return await authFromCode(gdrive, options);
} else {View on GitHub (pinned to 517fb7c5cf)
Solutions
- Retry the auth flow, ensuring only one launchWebAuthFlow is running at a time.
- Complete the Google consent screen fully rather than canceling or navigating away.
- Clear cached auth state/redirect data and start a fresh flow.
- If using a custom launchWebAuthFlow, confirm the final redirect URL preserves the state query parameter.
Example fix
// before
await gdrive.auth({ interactive: true }); // invalid_auth_response after canceled flow
// after
try {
await gdrive.auth({ interactive: true });
} catch (e) {
if (e.message === 'invalid_auth_response') {
await new Promise(r => setTimeout(r, 500)); // let stale flows settle
await gdrive.auth({ interactive: true });
} else throw e;
} Defensive patterns
Strategy: retry
Try / catch
try {
await gdrive.auth({ interactive: true });
} catch (e) {
if (e.message === 'invalid_auth_response') {
// stale/mismatched state: retry a fresh single flow
await gdrive.auth({ interactive: true });
} else throw e;
} Prevention
- Run only one launchWebAuthFlow at a time
- Ensure users complete the consent screen; detect cancellation separately
- Verify any custom auth flow preserves the state query parameter on redirect
When it happens
Trigger: The auth flow redirect URL carries a missing or different state parameter — e.g. a tampered/crafted redirect, a cached redirect from a previous flow, or a provider error page redirecting without state.
Common situations: Users aborting the Google consent screen and landing on an error redirect; browser restoring an old redirect URL; launching two overlapping auth flows where responses cross; custom authFlow implementations not preserving state.
Related errors
AI-assisted analysis of gildas-lormeau/SingleFile@517fb7c5cf (2026-09-01).
Data as JSON: /api/errors/2dc8ebcc88170d83.
Report an issue: GitHub.