{"record":{"id":"5f01ffd3fd304367","repo":"Budibase/budibase","slug":"you-must-set-a-spreadsheet-id-in-your-configuratio","errorCode":null,"errorMessage":"You must set a spreadsheet ID in your configuration to fetch tables.","messagePattern":"You must set a spreadsheet ID in your configuration to fetch tables\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/server/src/integrations/googlesheets.ts","lineNumber":199,"sourceCode":"    }\n  }\n\n  getBindingIdentifier() {\n    return \"\"\n  }\n\n  getStringConcat(_parts: string[]) {\n    return \"\"\n  }\n\n  /**\n   * Pull the spreadsheet ID out from a valid google sheets URL\n   * @param spreadsheetId - the URL or standard spreadsheetId of the google sheet\n   * @returns spreadsheet Id of the google sheet\n   */\n  private cleanSpreadsheetUrl(spreadsheetId: string) {\n    if (!spreadsheetId) {\n      throw new Error(\n        \"You must set a spreadsheet ID in your configuration to fetch tables.\"\n      )\n    }\n    const parts = spreadsheetId.split(\"/\")\n    return parts.length > 5 ? parts[5] : spreadsheetId\n  }\n\n  private async fetchAccessToken(\n    payload: AuthTokenRequest\n  ): Promise<AuthTokenResponse> {\n    const response = await fetch(\"https://www.googleapis.com/oauth2/v4/token\", {\n      method: \"POST\",\n      body: JSON.stringify({\n        ...payload,\n        grant_type: \"refresh_token\",\n      }),\n      headers: {\n        \"Content-Type\": \"application/json\",","sourceCodeStart":181,"sourceCodeEnd":217,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/integrations/googlesheets.ts#L181-L217","documentation":"cleanSpreadsheetUrl normalises either a full Google Sheets URL or a bare spreadsheet ID into a spreadsheet ID. If the configured spreadsheetId is empty/undefined there is nothing to parse and no sheet can be reached, so the constructor throws before any API call is made.","triggerScenarios":"Instantiating GoogleSheetsIntegration (constructor → cleanSpreadsheetUrl) with config.spreadsheetId undefined/null/empty string — typically when table fetching is triggered on a datasource that never had a spreadsheet selected.","commonSituations":"Datasource created via OAuth but the user never picked a spreadsheet; spreadsheetId field cleared after saving; fetching table names on a partially configured datasource; automation or script calling buildSchema without the spreadsheetId field.","solutions":["Open the Google Sheets datasource and select/paste a spreadsheet URL or ID, then re-fetch tables","Verify the saved datasource entity actually has a non-empty spreadsheetId property","If passing a URL, confirm it is a valid sheets URL (cleanSpreadsheetUrl splits on '/' and reads parts[5])","Guard calling code (testConnection/buildSchema) so it does not run until spreadsheetId is set"],"exampleFix":"// before\nconst ds = { type: 'GOOGLE_SHEETS', accessToken: token, spreadsheetId: undefined }\nnew GoogleSheetsIntegration(ds) // throws\n// after\nconst ds = { type: 'GOOGLE_SHEETS', accessToken: token, spreadsheetId: 'https://docs.google.com/spreadsheets/d/1AbC.../edit' }\nnew GoogleSheetsIntegration(ds)","handlingStrategy":"validation","validationCode":"function validateGSheetsConfig(config) {\n  if (!config.spreadsheetId || typeof config.spreadsheetId !== 'string') {\n    throw new Error('Google Sheets datasource requires a spreadsheetId or sheet URL')\n  }\n}","typeGuard":"function hasSpreadsheetId(config) {\n  return typeof config?.spreadsheetId === 'string' && config.spreadsheetId.trim().length > 0\n}","tryCatchPattern":"try {\n  const integration = new GoogleSheetsIntegration(config)\n} catch (err) {\n  if (err.message.includes('You must set a spreadsheet ID')) {\n    // redirect user to select a spreadsheet in the datasource UI\n  } else throw err\n}","preventionTips":["Never trigger buildSchema/testConnection until a spreadsheet is selected in the UI","Store the spreadsheetId at selection time, not lazily","Accept full sheet URLs but validate they parse to a non-empty ID"],"tags":["google-sheets","configuration","spreadsheet"],"backgroundTag":"missing-required-config","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}