{"record":{"id":"ed625111a7714922","repo":"usebruno/bruno","slug":"json-stringify-tokenresponse","errorCode":null,"errorMessage":"${JSON.stringify(tokenResponse)}","messagePattern":"\\$\\{JSON\\.stringify\\(tokenResponse\\)\\}","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/bruno-requests/src/auth/oauth2-helper.ts","lineNumber":382,"sourceCode":"    if (!autoFetchToken) {\n      // Don't fetch token if autoFetchToken is disabled\n      return null;\n    }\n    // Otherwise, proceed to fetch new token\n  }\n\n  let tokenResponse;\n\n  if (grantType === 'client_credentials') {\n    tokenResponse = await fetchTokenClientCredentials(oauth2Config, axiosInstance);\n  } else if (grantType === 'password') {\n    tokenResponse = await fetchTokenPassword(oauth2Config, axiosInstance);\n  } else {\n    throw new Error(`Unsupported grant type: ${grantType}`);\n  }\n\n  if (tokenResponse.error) {\n    throw new Error(JSON.stringify(tokenResponse));\n  }\n\n  if (!tokenResponse || !tokenResponse.access_token) {\n    throw new Error('No access token received from server');\n  }\n\n  if (tokenResponse.expires_in && tokenResponse.created_at) {\n    tokenResponse.expires_at = tokenResponse.created_at + tokenResponse.expires_in * 1000;\n  }\n\n  const saved = await tokenStore.saveCredential({ url: accessTokenUrl, credentialsId, credentials: tokenResponse });\n  if (!saved) {\n    console.warn('OAuth2: Failed to save token to store, but proceeding with token');\n  }\n\n  return tokenSource === 'id_token' ? tokenResponse.id_token : tokenResponse.access_token;\n};\n","sourceCodeStart":364,"sourceCodeEnd":400,"githubUrl":"https://github.com/usebruno/bruno/blob/9bdd81c7bdc57006e5f5ebffb79321a8d979f712/packages/bruno-requests/src/auth/oauth2-helper.ts#L364-L400","documentation":"Thrown when the token endpoint returned an HTTP response containing an 'error' field — i.e. the authorization server rejected the token request (invalid_client, invalid_grant, invalid_scope, etc.). The helper serializes the entire response body as JSON so the caller sees the server's error object verbatim.","triggerScenarios":"Token request reached the server but the server responded with an OAuth2 error body, e.g. {error:'invalid_client', error_description:'...'} because of wrong client secret, bad credentials, or an unauthorized scope.","commonSituations":"Wrong client secret; expired/revoked refresh material; requested scope not permitted for the client; clock skew causing assertion failures; user account locked when using password grant.","solutions":["Parse the thrown message as JSON to read error and error_description — the exact server reason is there.","For 'invalid_client': recheck clientId/clientSecret and credentialsPlacement (basic_auth_header vs body).","For 'invalid_grant' with password flow: confirm username/password are correct and the account is active.","For 'invalid_scope': trim the requested scopes to those registered for the client."],"exampleFix":"// before — opaque error bubbling up\ntry { await getOAuth2AccessToken(config, tokenStore); }\ncatch (e) { console.log(e.message); }\n\n// after — decode the server's error object\ntry { await getOAuth2AccessToken(config, tokenStore); }\ncatch (e) {\n  try {\n    const body = JSON.parse(e.message);\n    console.error('OAuth2 error:', body.error, '-', body.error_description);\n  } catch {\n    console.error('Unexpected error:', e.message);\n  }\n}","handlingStrategy":"try-catch","validationCode":"// Cannot fully prevent — server-side rejection. Validate inputs to reduce likelihood:\nif (config.credentialsPlacement && !['basic_auth_header','body'].includes(config.credentialsPlacement)) {\n  throw new Error('Bad credentialsPlacement');\n}","typeGuard":null,"tryCatchPattern":"try { await getOAuth2AccessToken(config, tokenStore); }\ncatch (e) {\n  let body;\n  try { body = JSON.parse(e.message); } catch { /* not a token-response error */ throw e; }\n  if (body && body.error) {\n    console.error('Token endpoint rejected:', body.error, body.error_description);\n    // branch on body.error: invalid_client -> fix secret, invalid_grant -> re-auth, etc.\n  } else throw e;\n}","preventionTips":["Log the parsed error body to classify token-endpoint failures.","Double-check credentialsPlacement matches what the server expects (header vs body).","Rotate client secrets on a schedule so expired secrets surface in tooling, not at runtime."],"tags":["oauth2","token-endpoint","server-error","runtime"],"backgroundTag":null,"analyzedSha":"9bdd81c7bdc57006e5f5ebffb79321a8d979f712","analyzedAt":"2026-08-13T04:09:25.751Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}