{"record":{"id":"cff65e217018390b","repo":"nexu-io/open-design","slug":"token-endpoint-rejected-request-http-res-status","errorCode":null,"errorMessage":"token endpoint rejected request: HTTP ${res.status} ${res.statusText} ${txt}","messagePattern":"token endpoint rejected request: HTTP (.+?) (.+?) (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/daemon/src/mcp-oauth.ts","lineNumber":424,"sourceCode":"  const headers: Record<string, string> = {\n    'content-type': 'application/x-www-form-urlencoded',\n    accept: 'application/json',\n  };\n  if (clientSecret) {\n    // RFC 6749 §2.3.1 — confidential clients use HTTP Basic with the\n    // client_id we already put in the form. Public clients (PKCE-only)\n    // skip this branch.\n    const basic = Buffer.from(`${form.get('client_id')}:${clientSecret}`).toString('base64');\n    headers['authorization'] = `Basic ${basic}`;\n  }\n  const res = await fetchImpl(tokenEndpoint, {\n    method: 'POST',\n    headers,\n    body: form.toString(),\n  });\n  if (!res.ok) {\n    const txt = await safeText(res);\n    throw new Error(\n      `token endpoint rejected request: HTTP ${res.status} ${res.statusText} ${txt}`,\n    );\n  }\n  const json = (await res.json()) as OAuthTokenResponse;\n  if (!json.access_token) {\n    throw new Error('token endpoint response missing access_token');\n  }\n  return json;\n}\n\nasync function safeText(res: Response): Promise<string> {\n  try {\n    const t = await res.text();\n    return t.slice(0, 500);\n  } catch {\n    return '';\n  }\n}","sourceCodeStart":406,"sourceCodeEnd":442,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/mcp-oauth.ts#L406-L442","documentation":"exchangeCodeForToken POSTs the authorization code plus PKCE verifier to the token endpoint. A non-2xx response fails with status, statusText, and up to 500 chars of body. Common reasons: expired or already-used auth code, wrong redirectUri, bad client_secret, PKCE mismatch, or invalid_grant.","triggerScenarios":"Replaying a single-use auth code; code expired; redirectUri in the token request differs from the authorize step; PKCE verifier mismatch; confidential client with the wrong secret.","commonSituations":"The OAuth callback was hit twice (retry or double-click); clock skew; the verifier was not preserved between beginAuth and the callback.","solutions":["Read the status and body in the message to distinguish invalid_grant from auth failure.","Ensure the auth code is exchanged exactly once and promptly.","Keep the same redirectUri and PKCE verifier used in beginAuth.","For confidential clients, confirm client_secret."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await exchangeCodeForToken(tokenEndpoint, params);\n} catch (e) {\n  if (/token endpoint rejected/i.test(e.message)) {\n    // a replayed or expired code yields invalid_grant -> restart the flow\n    if (/invalid_grant/i.test(e.message)) return beginAuth(input);\n  }\n  throw e;\n}","preventionTips":["Persist the PKCE verifier across the redirect.","Guard the OAuth callback against double-invocation.","Keep redirectUri identical between authorize and token exchange."],"tags":["oauth","token","network"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}