anomalyco/sst · error
Invalid grant_type
Error message
Invalid grant_type
What it means
SST's AuthHandler /token endpoint only implements the authorization_code grant. If the posted form's grant_type is anything other than "authorization_code", it returns HTTP 400 with this message. It guards the token exchange endpoint against unsupported or malformed token requests.
Source
Thrown at sdk/js/src/auth/handler.ts:233
Object.fromEntries((response.headers as any).entries())
);
},
cookie(c, key, value, maxAge) {
setCookie(c, key, value, {
maxAge,
httpOnly: true,
...(c.req.url.startsWith("https://")
? { secure: true, sameSite: "None" }
: {}),
});
},
};
app.post("/token", async (c) => {
console.log("token request");
const form = await c.req.formData();
if (form.get("grant_type") !== "authorization_code") {
c.status(400);
return c.text("Invalid grant_type");
}
const code = form.get("code");
if (!code) {
c.status(400);
return c.text("Missing code");
}
const { payload } = await jwtVerify(
code as string,
await options.signing.publicKey()
);
if (payload.redirect_uri !== form.get("redirect_uri")) {
c.status(400);
return c.text("redirect_uri mismatch");
}
if (payload.client_id !== form.get("client_id")) {
c.status(400);View on GitHub (pinned to a0bd20f762)
Solutions
- Send grant_type=authorization_code in the /token form body for the initial code exchange.
- If you need refresh tokens, implement refresh handling yourself or use a provider that supports it — this handler does not.
- Remove/replace client libraries that default to other grant types when talking to this endpoint.
- Confirm the request is form-encoded (application/x-www-form-urlencoded) with grant_type present, matching what the authorize step returned.
Example fix
// before
await fetch(`${authUrl}/token`, { method: "POST", body: new URLSearchParams({ grant_type: "refresh_token", refresh_token }) });
// after
await fetch(`${authUrl}/token`, {
method: "POST",
body: new URLSearchParams({ grant_type: "authorization_code", code, redirect_uri: cb, client_id: id }),
}); Defensive patterns
Strategy: validation
Validate before calling
// validate the token request body before sending
const body = new URLSearchParams({ grant_type: "authorization_code", code, redirect_uri, client_id });
if (body.get("grant_type") !== "authorization_code") throw new Error("Only authorization_code grant is supported"); Type guard
function isAuthorizationCodeGrant(form: URLSearchParams): boolean {
return form.get("grant_type") === "authorization_code";
} Try / catch
const res = await fetch(`${authUrl}/token`, { method: "POST", body });
if (res.status === 400) {
const msg = await res.text();
if (msg === "Invalid grant_type") throw new Error("Set grant_type=authorization_code");
} Prevention
- Only send grant_type=authorization_code to SST's /token endpoint.
- Disable automatic refresh-token flows in client libraries pointed at this handler.
- Send token requests as application/x-www-form-urlencoded form data.
- Pin the token-exchange helper to the fields this handler expects: grant_type, code, redirect_uri, client_id.
When it happens
Trigger: POSTing to /token with form field grant_type set to refresh_token, password, client_credentials, or missing entirely.
Common situations: A token client that auto-refreshes using grant_type=refresh_token, which this handler doesn't support; an OAuth client library sending client_credentials for machine flows; omitting grant_type in a hand-rolled token request; pointing a generic OIDC client at this endpoint.
Related errors
- Unsupported response_type: ${response_type}
- Missing code
- Missing token parameter
- error
- redirect_uri mismatch
AI-assisted analysis of anomalyco/sst@a0bd20f762 (2026-08-30).
Data as JSON: /api/errors/8fed901cc5131b1e.
Report an issue: GitHub.