{"record":{"id":"349279a01a7e7021","repo":"FlowiseAI/Flowise","slug":"failed-to-refresh-token-refreshresponse-status","errorCode":null,"errorMessage":"Failed to refresh token: ${refreshResponse.status} ${refreshResponse.statusText} - ${errorData}","messagePattern":"Failed to refresh token: (.+?) (.+?) - (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/components/src/utils.ts","lineNumber":1487,"sourceCode":"\n            try {\n                // Import fetch dynamically to avoid issues\n                const fetch = (await import('node-fetch')).default\n\n                // Call the refresh API endpoint\n                const refreshResponse = await fetch(\n                    `${options.baseURL || 'http://localhost:3000'}/api/v1/oauth2-credential/refresh/${credentialId}`,\n                    {\n                        method: 'POST',\n                        headers: {\n                            'Content-Type': 'application/json'\n                        }\n                    }\n                )\n\n                if (!refreshResponse.ok) {\n                    const errorData = await refreshResponse.text()\n                    throw new Error(`Failed to refresh token: ${refreshResponse.status} ${refreshResponse.statusText} - ${errorData}`)\n                }\n\n                await refreshResponse.json()\n\n                // Get the updated credential data\n                const updatedCredentialData = await getCredentialData(credentialId, options)\n\n                return updatedCredentialData\n            } catch (error) {\n                console.error('Failed to refresh access token:', error)\n                throw new Error(\n                    `Failed to refresh access token: ${\n                        error instanceof Error ? error.message : 'Unknown error'\n                    }. Please re-authorize the credential.`\n                )\n            }\n        }\n    }","sourceCodeStart":1469,"sourceCodeEnd":1505,"githubUrl":"https://github.com/FlowiseAI/Flowise/blob/abe4a8601a058047b350c260676826e21dd14101/packages/components/src/utils.ts#L1469-L1505","documentation":"Thrown when the internal OAuth2 refresh endpoint (POST /api/v1/oauth2-credential/refresh/<id>) responds with a non-2xx status. The message includes status code, status text, and the response body so the upstream refresh failure is visible. The provider itself (Google, Microsoft, GitHub, etc.) rejected the refresh attempt.","triggerScenarios":"Provider returns 400 invalid_grant (refresh token revoked, expired, or already used); 401 unauthorized_client (client secret changed); 400 invalid_client (misconfigured client ID/secret in Flowise); network error masqueraded as 5xx; refresh endpoint itself returned 404 because the credentialId doesn't exist.","commonSituations":"User revoked app access in the provider's account settings; client secret rotated in the provider console but not updated in Flowise; refresh token is single-use and was consumed by a concurrent request; the credential was deleted between the expiry check and the refresh call; Flowise server baseURL misconfigured so the request hits the wrong host.","solutions":["Read the embedded status + errorData: 400 invalid_grant means re-authorize; 401 means check client credentials.","Confirm options.baseURL points at the reachable Flowise API (defaults to http://localhost:3000).","If invalid_grant, re-authorize the OAuth2 credential — the refresh token is no longer usable.","Verify the OAuth client ID/secret stored in Flowise match the provider console.","Guard against concurrent refreshes (dedupe by credentialId) to avoid burning single-use refresh tokens."],"exampleFix":"// before\nif (!refreshResponse.ok) {\n  const errorData = await refreshResponse.text()\n  throw new Error(`Failed to refresh token: ${refreshResponse.status} ${refreshResponse.statusText} - ${errorData}`)\n}\n\n// after — classify common provider errors for the caller\nif (!refreshResponse.ok) {\n  const errorData = await refreshResponse.text()\n  let parsed: any = {}\n  try { parsed = JSON.parse(errorData) } catch {}\n  if (refreshResponse.status === 400 && parsed.error === 'invalid_grant') {\n    throw new Error('Refresh token is invalid or expired — re-authorize the OAuth2 credential.')\n  }\n  throw new Error(`Failed to refresh token: ${refreshResponse.status} ${refreshResponse.statusText} - ${errorData}`)\n}","handlingStrategy":"try-catch","validationCode":"function classifyRefreshFailure(status: number, body: string): 'invalid_grant' | 'client' | 'server' | 'unknown' {\n  let p: any = {}\n  try { p = JSON.parse(body) } catch {}\n  if (status === 400 && p.error === 'invalid_grant') return 'invalid_grant'\n  if (status === 401 || status === 400) return 'client'\n  if (status >= 500) return 'server'\n  return 'unknown'\n}","typeGuard":"function isProviderGrantError(body: string): boolean {\n  try { return JSON.parse(body)?.error === 'invalid_grant' } catch { return false }\n}","tryCatchPattern":"if (!refreshResponse.ok) {\n  const errorData = await refreshResponse.text()\n  const kind = classifyRefreshFailure(refreshResponse.status, errorData)\n  if (kind === 'invalid_grant') throw new Error('Refresh token invalid — re-authorize the credential.')\n  throw new Error(`Failed to refresh token: ${refreshResponse.status} ${refreshResponse.statusText} - ${errorData}`)\n}","preventionTips":["Configure options.baseURL explicitly to the reachable Flowise API.","Keep OAuth client ID/secret in sync with the provider console.","Dedupe concurrent refresh requests per credentialId for single-use refresh tokens."],"tags":["oauth2","refresh","http","provider","auth"],"backgroundTag":null,"analyzedSha":"abe4a8601a058047b350c260676826e21dd14101","analyzedAt":"2026-08-12T16:04:40.823Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}