{"record":{"id":"a15b646c4b3ea7f2","repo":"Budibase/budibase","slug":"invalid-tableid-tableid","errorCode":null,"errorMessage":"Invalid tableId: ${tableId}","messagePattern":"Invalid tableId: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/server/src/sdk/workspace/links/links.ts","lineNumber":13,"sourceCode":"import { context } from \"@budibase/backend-core\"\n\nimport { isTableIdOrExternalTableId } from \"@budibase/shared-core\"\nimport {\n  DatabaseQueryOpts,\n  LinkDocument,\n  LinkDocumentValue,\n} from \"@budibase/types\"\nimport { ViewName, getQueryIndex } from \"../../../db/utils\"\n\nexport async function fetch(tableId: string): Promise<LinkDocumentValue[]> {\n  if (!isTableIdOrExternalTableId(tableId)) {\n    throw new Error(`Invalid tableId: ${tableId}`)\n  }\n\n  const db = context.getWorkspaceDB()\n  const params: DatabaseQueryOpts = {\n    startkey: [tableId],\n    endkey: [tableId, {}],\n  }\n  const linkRows = (await db.query(getQueryIndex(ViewName.LINK), params)).rows\n  return linkRows.map(row => row.value as LinkDocumentValue)\n}\n\nexport async function fetchWithDocument(\n  tableId: string\n): Promise<LinkDocument[]> {\n  if (!isTableIdOrExternalTableId(tableId)) {\n    throw new Error(`Invalid tableId: ${tableId}`)\n  }\n","sourceCodeStart":1,"sourceCodeEnd":31,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/sdk/workspace/links/links.ts#L1-L31","documentation":"fetch(tableId) queries the link view keyed by table ID and rejects any tableId that fails isTableIdOrExternalTableId validation before touching the database. This guards CouchDB range queries from being built with garbage keys that would silently return wrong or empty link results.","triggerScenarios":"Calling sdk.workspace.links.fetch with undefined, an empty string, a non-table document ID (e.g. a datasource or row ID), or a malformed external table ID (e.g. missing the datasource prefix format expected by isTableIdOrExternalTableId).","commonSituations":"UI code passing a table name instead of its ID; plugins/automations passing row IDs; external (SQL) datasource tables whose IDs include an encoded connector prefix that was mangled before the call.","solutions":["Log the tableId and confirm it is a valid Budibase table ID (ta/... format or a valid external table ID)","Fetch the table via sdk.tables.get first to confirm it exists and use its _id","Check for undefined IDs caused by unawaited lookups or missing route params upstream"],"exampleFix":"// before\nconst links = await links.fetch(table.name) // not an ID\n// after\nconst links = await links.fetch(table._id!)","handlingStrategy":"validation","validationCode":"const isValidTableId = (id?: string) =>\n  typeof id === \"string\" && (id.startsWith(\"ta_\") || /^[a-z]+[a-zA-Z0-9_]*\\/|[a-f0-9-]{36}/.test(id))\nif (!isValidTableId(tableId)) throw new Error(`Refusing to fetch links for invalid tableId: ${tableId}`)","typeGuard":"const isTableId = (id: string | undefined): id is string =>\n  typeof id === \"string\" && id.length > 0 && id.startsWith(\"ta_\")","tryCatchPattern":"try {\n  const links = await links.fetch(tableId)\n} catch (err) {\n  if (err.message.startsWith(\"Invalid tableId\")) {\n    return [] // or re-resolve the table ID from the table document\n  }\n  throw err\n}","preventionTips":["Always pass table._id, never table names or row IDs","Resolve IDs from the tables API rather than trusting route params directly","For external tables, use the ID exactly as returned by the datasource"],"tags":["validation","links","table-id"],"backgroundTag":"invalid-identifier","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}