FlowiseAI/Flowise · error · Error
Google Sheets API Error ${response.status}: ${response.statu
Error message
Google Sheets API Error ${response.status}: ${response.statusText} - ${errorText} What it means
Thrown by BaseGoogleSheetsTool.makeGoogleSheetsRequest when the Google Sheets REST API (https://sheets.googleapis.com/v4/...) returns a non-2xx status. The message fuses the HTTP status code, status text, and the raw response body, so the underlying Google error JSON (e.g. {"error":{"code":403,"message":"...","status":"PERMISSION_DENIED"}}) is inlined verbatim. It is the single error path for every transport-level failure (auth, quota, not-found, validation) across all Spreadsheet/Values/Batch tools.
Source
Thrown at packages/components/nodes/tools/GoogleSheets/core.ts:155
}): Promise<string> {
const url = `https://sheets.googleapis.com/v4/${endpoint}`
const headers = {
Authorization: `Bearer ${this.accessToken}`,
'Content-Type': 'application/json',
Accept: 'application/json',
...this.headers
}
const response = await fetch(url, {
method,
headers,
body: body ? JSON.stringify(body) : undefined
})
if (!response.ok) {
const errorText = await response.text()
throw new Error(`Google Sheets API Error ${response.status}: ${response.statusText} - ${errorText}`)
}
const data = await response.text()
return data + TOOL_ARGS_PREFIX + JSON.stringify(params)
}
}
// Spreadsheet Tools
class CreateSpreadsheetTool extends BaseGoogleSheetsTool {
defaultParams: any
constructor(args: any) {
const toolInput = {
name: 'create_spreadsheet',
description: 'Create a new Google Spreadsheet',
schema: CreateSpreadsheetSchema,
baseUrl: '',
method: 'POST',View on GitHub (pinned to abe4a8601a)
Solutions
- Read the embedded Google error JSON first — the status field (PERMISSION_DENIED, NOT_FOUND, INVALID_ARGUMENT, RESOURCE_EXHAUSTED) names the exact cause; fix that, not the HTTP code.
- For 401/403, re-authorize the Google Sheets OAuth2 credential in the Flowise credential manager so a fresh accessToken is minted; confirm the credential's scopes include drive.file or spreadsheets.
- For 404, verify spreadsheetId — extract only the long ID segment from the URL between /d/ and /edit, not the whole URL.
- For 429, reduce concurrency, add exponential backoff between batch_get/batch_update calls, or request a quota increase in GCP.
- For 400 INVALID_ARGUMENT on ranges, ensure encodeURIComponent runs on params.range (it already does in update/append/clear; pass ranges like 'Sheet1!A1:B2').
Example fix
// before — token expired, gets 401
const tools = createGoogleSheetsTools({ accessToken: staleToken, actions })
// after — pass the Flowise credential so makeGoogleSheetsRequest receives a freshly refreshed token
const tools = createGoogleSheetsTools({ accessToken: await refreshAccessToken(credential), actions }) Defensive patterns
Strategy: try-catch
Validate before calling
// Before calling any Google Sheets tool, sanity-check the token and id shape
function assertSheetsReady(spreadsheetId: string, accessToken: string) {
if (!accessToken) throw new Error('accessToken missing — refresh the OAuth credential')
if (!/^[a-zA-Z0-9-_]{30,}$/.test(spreadsheetId)) throw new Error(`spreadsheetId looks malformed: ${spreadsheetId}`)
// optional cheap probe
return fetch(`https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}?fields=spreadsheetId`, {
headers: { Authorization: `Bearer ${accessToken}` }
}).then(r => { if (!r.ok) throw new Error(`probe failed: ${r.status}`) })
} Type guard
function isSheetsError(e: unknown): e is Error {
return e instanceof Error && /^Google Sheets API Error \d{3}:/.test(e.message)
} Try / catch
try {
return await tool.invoke(args)
} catch (e) {
if (isSheetsError(e)) {
const code = Number(e.message.match(/\b(\d{3})\b/)?.[1])
if (code === 401) await refreshAccessToken()
if (code === 429) await backoff()
if (code === 404) throw new Error(`spreadsheet not found — check id`, { cause: e })
}
throw e
} Prevention
- Refresh OAuth tokens before expiry; store expiry time alongside the token.
- Extract spreadsheetId with a regex from the URL to avoid paste errors.
- Add exponential backoff for 429/503 around batch_get/batch_update.
- Log the embedded Google error JSON, not just the HTTP code, for faster triage.
When it happens
Trigger: Any makeGoogleSheetsRequest call whose fetch resolves with response.ok === false. Concretely: 401 when the OAuth2 accessToken is expired/revoked, 403 when the service account lacks the spreadsheets scope or the user has no edit rights, 404 from a wrong/typo spreadsheetId or a deleted sheet, 400 from a malformed range (e.g. an un-encoded range with special chars reaching the encoded path), and 429/503 under API quota exhaustion. Each tool wraps the call in try/catch and converts it via formatToolError, so the agent sees a formatted error string rather than an exception bubble.
Common situations: Access token from a Google OAuth credential that expired (tokens live ~1h); a Spreadsheet shared with the wrong account so the service account can't open it; copy-pasting a spreadsheet URL instead of the bare ID; hitting the default 300 read/60 write requests-per-minute-per-user quota during batch jobs; a self-hosted/proxy environment that strips the Authorization header.
Related errors
- Jira API Error ${response.status}: ${response.statusText} -
- Unexpected error occurred while trying to ${action}. Status
- Failed to get spreadsheet metadata: ${response.status} ${res
- Failed to get sheet data: ${response.status} ${response.stat
- Oxylabs: Failed to call Oxylabs API: ${response.status}
AI-assisted analysis of FlowiseAI/Flowise@abe4a8601a (2026-08-12).
Data as JSON: /api/errors/c32cc7b026637773.
Report an issue: GitHub.