paperclipai/paperclip · error · Error
Spreadsheet ${spreadsheetId} is not in the configured allowl
Error message
Spreadsheet ${spreadsheetId} is not in the configured allowlist. What it means
createToolDefinitions builds an allowlist set of spreadsheet IDs and assertAllowed rejects any tool invocation whose spreadsheetId is not a member of that set. This is the server's primary data-boundary control: an agent or caller can only touch spreadsheets the operator explicitly pre-authorized at config time.
Source
Thrown at packages/google-sheets-mcp-server/src/tools.ts:136
return {
name,
description,
schema,
annotations: annotationsFor(description, risk),
execute: async (input) => {
try {
const parsed = schema.parse(input);
return formatTextResponse(await execute(parsed));
} catch (error) {
return formatErrorResponse(error, options.secretRedactions ?? []);
}
},
};
}
function assertAllowed(allowedSpreadsheetIds: Set<string>, spreadsheetId: string) {
if (!allowedSpreadsheetIds.has(spreadsheetId)) {
throw new Error(`Spreadsheet ${spreadsheetId} is not in the configured allowlist.`);
}
}
export function createToolDefinitions(options: GoogleSheetsToolOptions): GoogleSheetsToolDefinition[] {
const allowedSpreadsheetIds = Array.from(new Set(options.allowedSpreadsheetIds.map((id) => id.trim()).filter(Boolean)));
const allowedSpreadsheetIdSet = new Set(allowedSpreadsheetIds);
if (allowedSpreadsheetIds.length === 0) {
throw new Error("At least one allowed spreadsheet ID is required.");
}
return [
makeTool(
options,
"list_spreadsheets",
"List the Google Sheets spreadsheets configured in this connection allowlist.",
"read",
z.object({}),
async () => options.client.listSpreadsheets(allowedSpreadsheetIds),View on GitHub (pinned to 67001ec6eb)
Solutions
- Call the list_spreadsheets tool to see exactly which IDs are allowlisted and use one of those.
- Add the missing spreadsheet ID to the allowedSpreadsheetIds option (env var / config) and restart the server.
- Verify you are passing the spreadsheet ID (the middle path segment of the URL), not the full URL or the sheet/tab name.
Example fix
// before allowedSpreadsheetIds: ['1Bx...old'] // call uses new sheet -> error // after allowedSpreadsheetIds: ['1Bx...old', '9AbC...new']
Defensive patterns
Strategy: validation
Validate before calling
function isAllowed(spreadsheetId: string, allowed: string[]): boolean {
return allowed.includes(spreadsheetId);
}
if (!isAllowed(id, configuredAllow)) {
// surface the configured IDs to the user/agent instead of failing server-side
} Try / catch
try { await tools.call('delete_rows', { spreadsheetId: id, ... }) }
catch (e) {
if (/not in the configured allowlist/.test(String(e.message))) {
const allowed = await tools.call('list_spreadsheets');
// pick from allowed and retry, or escalate
} else throw e;
} Prevention
- Always call list_spreadsheets first when unsure which IDs are authorized.
- Pass full spreadsheet IDs, never URLs or tab names.
- Keep the configured allowlist and the IDs your agent references in the same config source.
When it happens
Trigger: Any tool that calls assertAllowed (e.g. delete_rows, clear_values, read/write tools) is invoked with a spreadsheetId that was not passed in options.allowedSpreadsheetIds when createToolDefinitions was constructed. Typo, wrong env var, copy-paste of an ID from another account, or a hallucinated ID all trigger it.
Common situations: Operator sets GOOGLE_SHEETS_SPREADSHEET_IDS to sheet A but the agent references sheet B; URL-key vs ID confusion (pasting the long docs URL instead of the /d/<id>/ segment); trailing whitespace in the configured ID that survived trim but the caller did not trim.
Related errors
- At least one allowed spreadsheet ID is required.
- Board access is required to resolve companies across the ins
- Creating companies requires board/instance-admin authenticat
- Request body too large.
- endIndex must be greater than startIndex.
AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12).
Data as JSON: /api/errors/18900a5a09ff5006.
Report an issue: GitHub.