{"record":{"id":"6db49d126c0f3db7","repo":"FlowiseAI/Flowise","slug":"access-token-is-expired-and-no-refresh-token-is-av","errorCode":null,"errorMessage":"Access token is expired and no refresh token is available. Please re-authorize the credential.","messagePattern":"Access token is expired and no refresh token is available\\. Please re-authorize the credential\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/components/src/utils.ts","lineNumber":1467,"sourceCode":" * @param {ICommonObject} credentialData\n * @param {ICommonObject} options\n * @param {number} bufferTimeMs - Buffer time in milliseconds before expiry (default: 5 minutes)\n * @returns {Promise<ICommonObject>}\n */\nexport const refreshOAuth2Token = async (\n    credentialId: string,\n    credentialData: ICommonObject,\n    options: ICommonObject,\n    bufferTimeMs: number = 5 * 60 * 1000\n): Promise<ICommonObject> => {\n    // Check if token is expired and refresh if needed\n    if (credentialData.expires_at) {\n        const expiryTime = new Date(credentialData.expires_at)\n        const currentTime = new Date()\n\n        if (currentTime.getTime() > expiryTime.getTime() - bufferTimeMs) {\n            if (!credentialData.refresh_token) {\n                throw new Error('Access token is expired and no refresh token is available. Please re-authorize the credential.')\n            }\n\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) {","sourceCodeStart":1449,"sourceCodeEnd":1485,"githubUrl":"https://github.com/FlowiseAI/Flowise/blob/abe4a8601a058047b350c260676826e21dd14101/packages/components/src/utils.ts#L1449-L1485","documentation":"Thrown by refreshOAuth2Token when credentialData.expires_at indicates the token is past the buffer window (5 min default) AND credentialData.refresh_token is absent. There is no way to silently refresh, so the only recovery is interactive re-authorization of the OAuth2 credential.","triggerScenarios":"The OAuth2 provider did not return a refresh_token at authorization time (some scopes/server flows omit it); the refresh_token was dropped during credential storage/serialization; the credential was authorized with a grant type that doesn't issue refresh tokens (e.g. implicit); a previous refresh consumed a single-use refresh token that wasn't persisted back.","commonSituations":"Google/Microsoft OAuth where offline_access scope wasn't requested; the provider's refresh token is single-use and the new one wasn't saved after the last refresh; credentials imported/exported without the refresh_token field; token expiry after a long Flowise downtime.","solutions":["Re-authorize the OAuth2 credential through Flowise to obtain a fresh access + refresh token pair.","When authorizing, request the offline_access / appropriate scope so the provider issues a refresh_token.","Ensure the credential persistence layer stores the refresh_token returned by each refresh (rotate single-use refresh tokens).","Increase bufferTimeMs if the issue is clock skew between Flowise and the provider."],"exampleFix":"// before\nif (!credentialData.refresh_token) {\n  throw new Error('Access token is expired and no refresh token is available. Please re-authorize the credential.')\n}\n\n// after — actionable message with the provider context\nif (!credentialData.refresh_token) {\n  throw new Error(\n    `Access token for credential ${credentialId} expired at ${credentialData.expires_at} and no refresh_token is stored. ` +\n    `Re-authorize the credential and ensure the OAuth scope includes offline_access/refresh_tokens.`\n  )\n}","handlingStrategy":"validation","validationCode":"function canRefreshOAuth2(cred: ICommonObject): boolean {\n  return Boolean(cred && typeof cred.refresh_token === 'string' && cred.refresh_token.length > 0)\n}","typeGuard":"function hasRefreshToken(cred: unknown): cred is { refresh_token: string; expires_at?: string } {\n  return typeof cred === 'object' && cred !== null && typeof (cred as any).refresh_token === 'string'\n}","tryCatchPattern":"if (currentTime.getTime() > expiryTime.getTime() - bufferTimeMs) {\n  if (!hasRefreshToken(credentialData)) {\n    throw new Error(`Access token expired at ${credentialData.expires_at}; no refresh_token stored. Re-authorize credential ${credentialId}.`)\n  }\n  // proceed with refresh\n}","preventionTips":["Request offline_access / refresh scope at authorization time.","Persist the rotated refresh_token after each refresh for single-use-token providers.","Surface a UI warning before tokens expire so users re-authorize proactively."],"tags":["oauth2","auth","tokens","refresh","credentials"],"backgroundTag":null,"analyzedSha":"abe4a8601a058047b350c260676826e21dd14101","analyzedAt":"2026-08-12T16:04:40.823Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}