{"record":{"id":"fa11657d2d9f8336","repo":"FlowiseAI/Flowise","slug":"failed-to-get-spreadsheet-metadata-response-sta","errorCode":null,"errorMessage":"Failed to get spreadsheet metadata: ${response.status} ${response.statusText} - ${errorText}","messagePattern":"Failed to get spreadsheet metadata: (.+?) (.+?) - (.+?)","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/components/nodes/documentloaders/GoogleSheets/GoogleSheets.ts","lineNumber":325,"sourceCode":"                finaltext += `${doc.pageContent}\\n`\n            }\n            return handleEscapeCharacters(finaltext, false)\n        }\n    }\n\n    private async getSpreadsheetMetadata(spreadsheetId: string, accessToken: string): Promise<any> {\n        const url = `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}`\n\n        const response = await fetch(url, {\n            headers: {\n                Authorization: `Bearer ${accessToken}`,\n                'Content-Type': 'application/json'\n            }\n        })\n\n        if (!response.ok) {\n            const errorText = await response.text()\n            throw new Error(`Failed to get spreadsheet metadata: ${response.status} ${response.statusText} - ${errorText}`)\n        }\n\n        return response.json()\n    }\n\n    private async getSheetData(spreadsheetId: string, range: string, valueRenderOption: string, accessToken: string): Promise<any> {\n        const url = `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${encodeURIComponent(range)}`\n        const params = new URLSearchParams({\n            valueRenderOption,\n            dateTimeRenderOption: 'FORMATTED_STRING',\n            majorDimension: 'ROWS'\n        })\n\n        const response = await fetch(`${url}?${params}`, {\n            headers: {\n                Authorization: `Bearer ${accessToken}`,\n                'Content-Type': 'application/json'\n            }","sourceCodeStart":307,"sourceCodeEnd":343,"githubUrl":"https://github.com/FlowiseAI/Flowise/blob/abe4a8601a058047b350c260676826e21dd14101/packages/components/nodes/documentloaders/GoogleSheets/GoogleSheets.ts#L307-L343","documentation":"Thrown by GoogleSheets.getSpreadsheetMetadata after a non-ok response from GET https://sheets.googleapis.com/v4/spreadsheets/{spreadsheetId}. The message includes the HTTP status code, status text, and the raw response body, so it is the authoritative source for why the Google Sheets API rejected the metadata call.","triggerScenarios":"401 when the access token is expired/revoked; 403 when the token lacks spreadsheets.readonly scope or the spreadsheet is not shared with the service account email; 404 when spreadsheetId is wrong or the sheet was deleted; 429 when the per-user/per-project quota is exhausted.","commonSituations":"Token auto-refresh not wired up so a cached token goes stale; service account email not added as a viewer on the target sheet; user copies only part of the spreadsheetId (e.g. includes the #gid fragment); hitting the Sheets API read quota during a large crawl.","solutions":["Decode the status code in the message: 401 → re-authorize/refresh the OAuth token; 403 → add the service account email as a viewer on the sheet and confirm spreadsheets.readonly scope; 404 → correct the spreadsheetId; 429 → add exponential backoff or reduce request volume.","Confirm the credential uses the same Google Cloud project that enabled the Google Sheets API.","Ensure the access token is fetched at call time, not cached across long-lived sessions.","Add retry-with-backoff around getSpreadsheetMetadata specifically for 429/503 responses."],"exampleFix":"// before\nconst response = await fetch(url, { headers })\nif (!response.ok) {\n  const errorText = await response.text()\n  throw new Error(`Failed to get spreadsheet metadata: ${response.status} ${response.statusText} - ${errorText}`)\n}\n// after - retry transient failures\nasync function fetchWithRetry(url, opts, attempts = 3) {\n  for (let i = 0; i < attempts; i++) {\n    const r = await fetch(url, opts)\n    if (r.ok || (r.status !== 429 && r.status < 500)) return r\n    await new Promise(res => setTimeout(res, 2 ** i * 500))\n  }\n  const r = await fetch(url, opts)\n  return r\n}","handlingStrategy":"retry","validationCode":"// Validate the token will likely work: call a cheap userinfo endpoint first\nasync function tokenLooksValid(accessToken) {\n  const r = await fetch('https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=' + accessToken)\n  return r.ok\n}","typeGuard":"function isHttpError(e, statusStart = 400) {\n  return typeof e?.message === 'string' && /Failed to get spreadsheet metadata:\\s*(\\d{3})/.test(e.message)\n    && Number(RegExp.$1) >= statusStart\n}","tryCatchPattern":"async function getMetadataWithRetry(id, token, attempts = 3) {\n  for (let i = 0; i < attempts; i++) {\n    try { return await loader.getSpreadsheetMetadata(id, token) }\n    catch (e) {\n      const m = e.message.match(/(\\d{3})/)\n      const code = m ? Number(m[1]) : 0\n      if ((code === 429 || code >= 500) && i < attempts - 1) {\n        await new Promise(r => setTimeout(r, 2 ** i * 500)); continue\n      }\n      throw e\n    }\n  }\n}","preventionTips":["Request spreadsheets.readonly scope on the OAuth credential.","Share the spreadsheet with the service account email.","Refresh access tokens at call time rather than caching long-term.","Add backoff for 429/5xx responses."],"tags":["google-sheets","http","oauth","api-status","quota"],"backgroundTag":null,"analyzedSha":"abe4a8601a058047b350c260676826e21dd14101","analyzedAt":"2026-08-12T16:04:40.823Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}