anomalyco/sst · error · OauthError

error

Error message

error

What it means

In the OAuth adapter's form_post callback route, if the identity provider POSTs an `error` field in the form body, the adapter rethrows it as an `OauthError`. This surfaces provider-side OAuth failures (per RFC 6749) such as `access_denied`, `invalid_request`, or `server_error` back to your handler with the raw provider error code as the message.

Source

Thrown at sdk/js/src/auth/adapter/oauth.ts:103

          config.issuer.metadata.userinfo_endpoint
            ? "callback"
            : "oauthCallback"
        ](callback.toString(), query, {
          code_verifier,
          state,
        });
        return ctx.success(c, {
          client,
          tokenset,
        });
      });

      // response_mode=form_post
      routes.post("/callback", async (c) => {
        const [callback, client] = getClient(c);
        const form = await c.req.formData();
        if (form.get("error")) {
          throw new OauthError(form.get("error")!.toString());
        }
        const code_verifier = getCookie(c, "auth_code_verifier");
        const state = getCookie(c, "auth_state");
        const tokenset = await client[
          config.issuer.metadata.userinfo_endpoint
            ? "callback"
            : "oauthCallback"
        ](callback.toString(), Object.fromEntries(form as any), {
          code_verifier,
          state,
        });
        return ctx.success(c, {
          client,
          tokenset,
        });
      });
    } satisfies Adapter<{
      tokenset: TokenSet;

View on GitHub (pinned to a0bd20f762)

Solutions

  1. Read `error.response`/the OauthError message to identify the provider error code and handle it (e.g. show a 'consent denied' UI for access_denied).
  2. Verify your OAuth client config: client ID, secret, redirect URI, and requested scopes match the provider's registered values.
  3. Restart the login flow from /authorize — OAuth errors on callback are not retryable with the same request.
  4. Check the provider dashboard/logs for the corresponding authorization failure detail.

Example fix

try {
  await client.auth.<provider>.authorize(...)
} catch (e) {
  if (e instanceof OauthError && e.message === "access_denied") {
    return new Response("You must grant access to continue", { status: 403 });
  }
  throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Check before redirecting to the IdP that client config is registered correctly
const redirect = await client.authorizationUrl({ redirect_uri, scope });
if (!redirect) throw new Error("OAuth client not configured");

Type guard

function isOauthError(e: unknown): e is OauthError {
  return e instanceof OauthError;
}

Try / catch

try {
  await completeLogin(c);
} catch (e) {
  if (e instanceof OauthError) {
    if (e.message === "access_denied") return c.text("Access denied by user", 403);
    return c.text("OAuth error: " + e.message, 502);
  }
  throw e;
}

Prevention

When it happens

Trigger: An IdP POSTs to `/<auth-name>/callback` (response_mode=form_post) with `error` in the form body instead of `code`/`state`. Happens when the user denies consent, the authorization request was malformed, scopes are invalid, or the provider errors out during authorization.

Common situations: User cancels the consent screen (`access_denied`); misconfigured redirect URI or client ID (`invalid_request`, `unauthorized_client`); requesting scopes the app isn't approved for; IdP outage (`temporarily_unavailable`); expired or replayed authorization flow.

Related errors


AI-assisted analysis of anomalyco/sst@a0bd20f762 (2026-08-30). Data as JSON: /api/errors/ba4c8d9c9b15b6c3. Report an issue: GitHub.