Error Reference
Every error sheets-mcp can return — exact message text from source, cause, and agent recovery steps.
Note
AX note for agents. All errors return as structured JSON inside the MCP
contentarray withisError: true. Parse theerrorfield first. IfneedsAuth: trueis present, surface the dashboard URL immediately — no tool call can succeed until the user re-authorizes.
Error envelope
{
"content": [{
"type": "text",
"text": "{ \"error\": \"<message>\", \"stack\": [\"...\"] }"
}],
"isError": true
}For auth expiry, needsAuth: true is added:
{
"content": [{
"type": "text",
"text": "{ \"error\": \"Google Authentication Expired...\", \"needsAuth\": true }"
}],
"isError": true
}Authentication errors
Google Authentication Expired
Exact message:
Google Authentication Expired. Your OAuth connection has expired or been revoked.
Please visit the dashboard (https://sheets-mcp-xi.vercel.app/) and sign in again
to refresh your connection.Cause: The Google OAuth refresh token was revoked (user removed app access from Google Account settings), or the googleapis client returned invalid_grant / Invalid Credentials.
Agent fix: Set needsAuth: true flag triggers this path. Tell the user to visit https://sheets-mcp-xi.vercel.app/dashboard, sign out, and re-authorize with Google.
No session found
Cause: withMcpAuth could not resolve a session from the Bearer JWT or x-api-key header.
Agent fix: Re-authenticate. For OIDC clients, restart the MCP client and complete OAuth consent. For API key clients, verify x-api-key header is present and valid.
Google access token not found in DB. User must re-authenticate with Google.
Cause: The better-auth session exists but the account table has no Google OAuth token for this user — the Google OAuth callback was interrupted or the account row was deleted.
Agent fix: User must sign out from the dashboard and re-authorize Google. Same as needsAuth: true recovery flow.
Write safety errors
Formula overwrite blocked
Exact message pattern (from formulaOverwriteBlockedMessage()):
<toolName> blocked because the target range contains formula cells (): <cell list>.
Pass allowFormulaOverwrite: true only if you intend to replace formulas with static values.Example:
write_range blocked because the target range contains formula cells (3): Sheet1!B2, Sheet1!B3, Sheet1!B4.
Pass allowFormulaOverwrite: true only if you intend to replace formulas with static values.Cause: Target range contains cells starting with =. Detected by fetching the range with valueRenderOption: FORMULA before writing.
Agent fix: Check _schema.arrayFormulaColumns and _schema.protectedRanges from a prior read_range call. Adjust range to avoid formula cells, or set allowFormulaOverwrite: true if intentional.
ARRAYFORMULA spill blocked
Exact message pattern (from arrayFormulaSpillBlockedMessage()):
ARRAYFORMULA spill blocked. Formula cell(s): <cell list>.
Potential spill would overwrite occupied cells (): <cell list>.
Clear those cells, insert buffer columns, narrow the formula output,
or pass allowFormulaOverwrite: true if this overwrite is intentional.Cause: The incoming values[][] contains an ARRAYFORMULA that would spill into cells already occupied.
Agent fix: The error message lists exact cells to clear. Either clear them first or set allowFormulaOverwrite: true.
ARRAYFORMULA column guard (schema-level)
Exact message (from runPreWriteSchemaGuards()):
⚠ A target column contains an ARRAYFORMULA. Writing static values will permanently
destroy this formula. Pass allowFormulaOverwrite: true to confirm you intend to
replace the formula with static data.Cause: The write footprint overlaps a column where row 2 contains an ARRAYFORMULA anchor (detected from the schema cache).
Agent fix: Check _schema.warnings from read_range first. Those warnings list exactly which columns to avoid.
Protected Range — strict lock
Exact message (from runPreWriteSchemaGuards()):
Write blocked — range overlaps a Protected Range (locked). The sheet owner has
locked this range. This cannot be overridden.Cause: warningOnly: false protected range overlaps the write target.
Agent fix: The user must unlock the range in Google Sheets UI. Not bypassable via API. Narrow the write range to avoid the protected area.
Protected Range — warning only (non-blocking)
When warningOnly: true, the write proceeds but _warnings is added to the response:
{ "updatedCells": 6, "_warnings": ["⚠ Target range has a user-set protection warning. Proceeding as requested."] }Mid-operation protection conflict (write_range only)
Write failed — a protected range conflict was detected mid-operation.
The sheet's protection settings may have changed.
Call read_range to re-inspect the current schema before retrying.Cause: A 403 from the Sheets API occurred after the pre-write guard passed — meaning the sheet was modified between the guard check and the write execution.
Agent fix: Call read_range on the range to get a fresh _schema, then retry.
transform_range cell limit
Too many cells (). Maximum is 50,000 per transform call.Cause: rows × columns in the range exceeds 50,000 cells.
Agent fix: Narrow the range to fewer columns (e.g. Sheet1!A:D instead of Sheet1!A:Z), or process in multiple passes.
transform_range zero rows
SQL returned 0 rows. No changes written.Cause: The SQL SELECT returned an empty result. The sheet was not modified — this is a safe no-op.
Agent fix: Review the WHERE clause. Use analyze_range with the same query to debug before retrying transform_range.
manage_sheets errors
Missing required fields
title is required for action=add
title is required for action=rename
sheetId is required for action=rename
sheetId is required for action=deleteAgent fix: Always call list_sheets first to get numeric sheetId values before rename or delete.
Delete blocked (non-empty sheet)
Exact message pattern:
Delete blocked: sheet "<title>" contains rows of data.
Pass confirmDelete: true to permanently delete this sheet. THIS CANNOT BE UNDONE.Cause: manage_sheets counted rows in column A before executing the delete. Sheet has data and confirmDelete was not true.
Agent fix: Pass confirmDelete: true to confirm. Warn the user this is irreversible — there is no restore_snapshot for sheet tab deletion.
Rename formula-ref warning (non-blocking)
After a successful rename, _warnings is always returned:
{
"ok": true,
"action": "rename",
"_warnings": [
"If other sheets contain formulas referencing the old name, they may be broken.
Google Sheets auto-updates references on rename via the UI but NOT via the API."
]
}This is not an error — the rename succeeded. But cross-sheet formula references may now be broken.
restore_snapshot errors
snapshotId is required for restore
Cause: Called with action: "restore" but no snapshotId provided.
Agent fix: Call restore_snapshot(action: "list") first, then use the UUID from the response.
Snapshot not found
Cause: The snapshotId UUID does not exist in write_history for the current user+spreadsheet combination. The snapshot may have been pruned (max 50 retained).
Agent fix: Call restore_snapshot(action: "list") to see currently available snapshots.
SQL errors
SQL injection guard (silent + error)
;is stripped before the query reaches Postgres — no error, but statement chaining is silently preventedDROP,DELETE,UNIONpatterns are rejected — results in a Postgres parse error or the keyword is stripped
Agent fix: Never pass raw user strings directly into SQL. Use only data ->>'column_name' JSONB lookups.
Google Sheets API pass-through errors
These originate from the Sheets API v4 and are returned as-is in the error field:
| Message | Cause |
|---|---|
The caller does not have permission | Spreadsheet not in user's Drive, or read-only sharing |
Requested entity was not found | Invalid spreadsheetId or tab deleted |
Unable to parse range | Malformed A1 notation — check sheet name and cell refs |
Quota exceeded | 60 requests/min/user exceeded |
No grid with id: | sheetId number no longer exists |
Tip:
When
No grid with id: Noccurs, sheets-mcp auto-appends the list of current valid sheet IDs and titles usingformatSheetIdNotFoundMessage():
sheetId 999 not found in spreadsheet.
Use list_sheets to get the correct sheetId before retrying.
Available sheets: Sheet1 (0), Revenue (812345678), Archive (923456789).Agents can parse the Available sheets suffix to self-correct without an extra list_sheets call.