{"record":{"id":"f49e9e9db038d44a","repo":"Budibase/budibase","slug":"table-updated-externally-please-re-fetch-err","errorCode":null,"errorMessage":"Table updated externally, please re-fetch - ${err.message}","messagePattern":"Table updated externally, please re-fetch - (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/server/src/sdk/workspace/rows/search/external.ts","lineNumber":165,"sourceCode":"      )\n    const allowedFields = [...visibleFields, ...PROTECTED_EXTERNAL_COLUMNS]\n    processed = processed.map(r => pick(r, allowedFields))\n\n    // need wrapper object for bookmarks etc when paginating\n    const response: SearchResponse<Row> = { rows: processed, hasNextPage }\n    if (hasNextPage && bookmark != null) {\n      response.bookmark = bookmark + processed.length\n    }\n    if (totalRows != null) {\n      response.totalRows = totalRows\n    }\n    if (paginate && !hasNextPage) {\n      response.hasNextPage = false\n    }\n    return response\n  } catch (err: any) {\n    if (err.message && err.message.includes(\"does not exist\")) {\n      throw new Error(\n        `Table updated externally, please re-fetch - ${err.message}`,\n        { cause: err }\n      )\n    } else {\n      throw err\n    }\n  }\n}\n\nexport async function exportRows(\n  options: ExportRowsParams\n): Promise<ExportRowsResult> {\n  const {\n    tableId,\n    format,\n    columns,\n    rowIds,\n    query,","sourceCodeStart":147,"sourceCodeEnd":183,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/sdk/workspace/rows/search/external.ts#L147-L183","documentation":"When an external SQL query fails because the underlying table 'does not exist' (schema drift between Budibase metadata and the actual datasource), search wraps the error with a message telling the developer the table was updated externally and to re-fetch.","triggerScenarios":"Running a search against an external table whose columns/table were renamed or dropped directly in the database after the Budibase datasource was configured.","commonSituations":"DBA renames a column in Postgres; migrations run outside Budibase; datasource entities cached from a previous schema; table dropped between fetches.","solutions":["Re-fetch/sync the datasource schema in Budibase so entities match the live DB","Restore the renamed/dropped table or column in the database","Check the cause err.message to identify which relation/column is missing","Reconfigure the datasource connection if the database changed"],"exampleFix":"// before\nconst res = await sdk.rows.search({ tableId, query }) // throws after external rename\n// after\ntry {\n  const res = await sdk.rows.search({ tableId, query })\n} catch (e) {\n  if (e.message.includes('Table updated externally')) await sdk.datasources.sync(datasourceId)\n  else throw e\n}","handlingStrategy":"try-catch","validationCode":"const ds = await sdk.datasources.get(datasourceId)\nconst liveEntities = Object.keys(ds.entities || {})\nif (!liveEntities.includes(tableName)) await sdk.datasources.sync(datasourceId)","typeGuard":null,"tryCatchPattern":"try { return await search(...) } catch (e) { if (e.message.includes('Table updated externally')) { await sdk.datasources.sync(datasourceId); return await search(...) } throw e }","preventionTips":["Sync datasources after external DB migrations","Monitor for 'does not exist' errors as a sync trigger","Avoid renaming/dropping tables without updating Budibase","Cache datasource metadata with a TTL"],"tags":["schema-drift","external-tables","sql"],"backgroundTag":"schema-drift-out-of-sync","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}