{"record":{"id":"686038d2ea092fcf","repo":"Budibase/budibase","slug":"view-viewid-not-found-in-tableid","errorCode":null,"errorMessage":"View '${viewId}' not found in '${tableId}'","messagePattern":"View '(.+?)' not found in '(.+?)'","errorType":"http","errorClass":"HTTPError","httpStatus":400,"severity":"error","filePath":"packages/server/src/sdk/workspace/rowActions/crud.ts","lineNumber":182,"sourceCode":"\n  const updated = await transformer(actionsDoc)\n\n  const db = context.getWorkspaceDB()\n  await db.put(updated)\n\n  return {\n    id: rowActionId,\n    ...updated.actions[rowActionId],\n  }\n}\n\nasync function guardView(tableId: string, viewId: string) {\n  let view\n  if (isViewId(viewId)) {\n    view = await sdk.views.get(viewId)\n  }\n  if (!view || view.tableId !== tableId) {\n    throw new HTTPError(`View '${viewId}' not found in '${tableId}'`, 400)\n  }\n}\n\nexport async function setTablePermission(tableId: string, rowActionId: string) {\n  return await updateDoc(tableId, rowActionId, async actionsDoc => {\n    actionsDoc.actions[rowActionId].permissions.table.runAllowed = true\n    return actionsDoc\n  })\n}\n\nexport async function unsetTablePermission(\n  tableId: string,\n  rowActionId: string\n) {\n  return await updateDoc(tableId, rowActionId, async actionsDoc => {\n    actionsDoc.actions[rowActionId].permissions.table.runAllowed = false\n    return actionsDoc\n  })","sourceCodeStart":164,"sourceCodeEnd":200,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/sdk/workspace/rowActions/crud.ts#L164-L200","documentation":"Thrown by guardView in packages/server/src/sdk/workspace/rowActions/crud.ts when setting or unsetting a view-level permission for a view that either does not exist or does not belong to the given table. guardView only resolves the view when isViewId(viewId) is true, then verifies view.tableId matches tableId.","triggerScenarios":"Calling setViewPermission/unsetViewPermission with a viewId that fails isViewId (so view stays undefined), a deleted view id, or a valid view id whose view.tableId differs from the supplied tableId.","commonSituations":"Passing a table id where a view id is expected; view deleted from the table while the client still holds its id; cross-table mix-up after copying permission code between tables.","solutions":["Pass a valid view id (formatted as a view id, e.g. 'data_view_...') rather than a table or row id.","Confirm the view belongs to the same tableId you pass; fetch the view via sdk.views.get and compare tableId.","Re-fetch the table's views list if the view may have been deleted recently."],"exampleFix":"// before\nawait rowActions.setViewPermission(tableId, rowActionId, tableId /* wrong: table id as viewId */)\n// after\nconst viewId = table.views[\"my_view\"]?.id\nif (viewId && isViewId(viewId)) await rowActions.setViewPermission(tableId, rowActionId, viewId)","handlingStrategy":"validation","validationCode":"if (!isViewId(viewId)) throw new Error(\"setViewPermission requires a view id\")\nconst view = await sdk.views.get(viewId)\nif (!view || view.tableId !== tableId) throw new Error(`View ${viewId} is not part of table ${tableId}`)","typeGuard":"const isValidViewForTable = (view: View | undefined, tableId: string): view is View =>\n  !!view && view.tableId === tableId","tryCatchPattern":"try {\n  await rowActions.setViewPermission(tableId, rowActionId, viewId)\n} catch (e) {\n  if (e instanceof HTTPError && e.status === 400) {\n    // wrong or missing view id: re-resolve from table.views\n  } else throw e\n}","preventionTips":["Resolve view ids from table.views rather than passing table or row ids.","Use isViewId to validate id format before any view-scoped call.","Verify view.tableId matches your target table before permission changes."],"tags":["not-found","views","row-actions","http-400","id-validation"],"backgroundTag":"resource-not-found","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}