usebruno/bruno · error · Error
${response.error}
Error message
${response.error} What it means
Thrown by handleOAuth2Response inside the renderer:fetch-oauth2-credentials flow when the token endpoint response carries an `.error` field but no `.debugInfo`. This is a non-recoverable OAuth2 error surfaced from the identity provider (e.g. invalid_grant, invalid_client, bad_verification_code). Responses with debugInfo are passed through so callers can retry with diagnostics.
Source
Thrown at packages/bruno-electron/src/ipc/collection.js:2013
processEnvVars,
promptVariables
});
let refreshRequestForConfig = { ...requestCopy, url: interpolatedRefreshUrl };
certsAndProxyConfigForRefreshUrl = await getCertsAndProxyConfig({
collectionUid,
collection,
request: refreshRequestForConfig,
envVars,
runtimeVariables,
processEnvVars,
collectionPath,
globalEnvironmentVariables
});
}
const handleOAuth2Response = (response) => {
if (response.error && !response.debugInfo) {
throw new Error(response.error);
}
return response;
};
switch (grantType) {
case 'authorization_code':
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars);
return await getOAuth2TokenUsingAuthorizationCode({
request: requestCopy,
collectionUid,
forceFetch: true,
certsAndProxyConfigForTokenUrl,
certsAndProxyConfigForRefreshUrl
}).then(handleOAuth2Response);
case 'client_credentials':
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars);
return await getOAuth2TokenUsingClientCredentials({View on GitHub (pinned to 9bdd81c7bd)
Solutions
- Re-trigger the authorization_code flow — codes are single-use and short-lived.
- Confirm clientId, clientSecret, and token URL match the IdP app registration.
- Verify the redirect URI sent matches what is registered with the provider.
- If using refresh_token, prompt the user to re-authenticate when the token has been revoked.
- Read response.error verbatim — it is the IdP's RFC 6749 error code (e.g. invalid_grant).
Defensive patterns
Strategy: try-catch
Type guard
/** @typedef {{ error?: string, debugInfo?: unknown, access_token?: string }} OAuth2Response */
/** @param {OAuth2Response} r @returns {r is { error: string }} */
function isOAuth2ErrorResponse(r) {
return !!r && typeof r.error === 'string' && !r.debugInfo;
} Try / catch
try {
const token = await ipcRenderer.invoke('renderer:fetch-oauth2-credentials', { itemUid, request, collection });
return token;
} catch (err) {
// err.message is the IdP's RFC 6749 error code (e.g. 'invalid_grant')
if (/invalid_grant|expired/.test(err.message)) {
// re-authenticate the user
} else if (/invalid_client/.test(err.message)) {
// surface a credentials-config error
}
throw err;
} Prevention
- Refresh tokens before they expire; do not let the auth code sit beyond its lifetime.
- Keep clientId, clientSecret, token URL, and redirect URI in lockstep with the IdP registration.
- Sync the system clock (NTP) to avoid IdP clock-skew rejections.
- Treat presence of `.debugInfo` as retryable; absence as terminal — surface accordingly.
When it happens
Trigger: Any OAuth2 grant (authorization_code, refresh_token, client_credentials, password) where the provider returns `{ error: '...' }` without a debug payload — typically expired codes, revoked refresh tokens, redirect URI mismatch, or wrong client credentials.
Common situations: Auth code used after expiry (10-minute window), refresh token revoked out-of-band, clock skew against the IdP, client secret rotated but not updated, redirect URI not whitelisted.
Related errors
- Invalid token URL: ${requestConfig.url}
- Failed to export ${environmentType} environments.
- Could not reach the mock server
- Mock server id is required.
- Workspace path is required.
AI-assisted analysis of usebruno/bruno@9bdd81c7bd (2026-08-13).
Data as JSON: /api/errors/f078d395e05a99c3.
Report an issue: GitHub.