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

  1. Retry the auth flow, ensuring only one launchWebAuthFlow is running at a time.
  2. Complete the Google consent screen fully rather than canceling or navigating away.
  3. Clear cached auth state/redirect data and start a fresh flow.
  4. 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

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.