{"record":{"id":"13ad431498b54d22","repo":"Budibase/budibase","slug":"a-row-action-with-the-same-name-already-exists","errorCode":null,"errorMessage":"A row action with the same name already exists.","messagePattern":"A row action with the same name already exists\\.","errorType":"http","errorClass":"HTTPError","httpStatus":409,"severity":"error","filePath":"packages/server/src/sdk/workspace/rowActions/crud.ts","lineNumber":37,"sourceCode":"import { generateRowActionsID } from \"../../../db/utils\"\nimport automations from \"../automations\"\n\nasync function ensureUniqueAndThrow(\n  doc: TableRowActions,\n  name: string,\n  existingRowActionId?: string\n) {\n  const names = await getNames(Object.values(doc.actions))\n  name = name.toLowerCase().trim()\n\n  if (\n    Object.entries(names).find(\n      ([automationId, automationName]) =>\n        automationName.toLowerCase().trim() === name &&\n        automationId !== existingRowActionId\n    )\n  ) {\n    throw new HTTPError(\"A row action with the same name already exists.\", 409)\n  }\n}\n\nexport async function create(tableId: string, rowAction: { name: string }) {\n  const action = { name: rowAction.name.trim() }\n\n  const db = context.getWorkspaceDB()\n  const rowActionsId = generateRowActionsID(tableId)\n  let doc = await db.tryGet<TableRowActions>(rowActionsId)\n  if (!doc) {\n    doc = { _id: rowActionsId, actions: {} }\n  }\n\n  await ensureUniqueAndThrow(doc, action.name)\n\n  const workspaceId = context.getWorkspaceId()\n  if (!workspaceId) {\n    throw new Error(\"Could not get the current workspace ID\")","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/sdk/workspace/rowActions/crud.ts#L19-L55","documentation":"Thrown by ensureUniqueAndThrow (called from create) in packages/server/src/sdk/workspace/rowActions/crud.ts when creating a row action whose name (lowercased and trimmed) matches an existing row action name on the same table, unless it belongs to the row action being updated. Row action names must be unique per table because they are referenced by name in generated CRUD views/permissions.","triggerScenarios":"Calling create(tableId, { name }) where any existing automation/row-action name on that table equals name.toLowerCase().trim(). The comparison is case-insensitive and whitespace-trimmed, so 'Run Script' collides with 'run script '.","commonSituations":"Users adding a second action with the same label on one table; scripts bulk-creating actions without a uniqueness check; renaming conflicts where the create path is used instead of update.","solutions":["Pick a different name for the row action that no other action on the table uses (case-insensitively).","Before creating, fetch existing row actions for the table and check the trimmed lowercase name for collisions.","If you meant to modify an existing action, call the update API instead of create."],"exampleFix":"// before\nawait rowActions.create(tableId, { name: \"Approve\" })\n// after\nconst existing = await rowActions.getAllForTable(tableId)\nconst clash = Object.values(existing?.actions ?? {}).some(a => a.name.toLowerCase().trim() === \"approve\")\nif (!clash) await rowActions.create(tableId, { name: \"Approve\" })","handlingStrategy":"validation","validationCode":"const actionsDoc = await rowActions.getAllForTable(tableId)\nconst normalized = name.toLowerCase().trim()\nif (Object.values(actionsDoc?.actions ?? {}).some(a => a.name.toLowerCase().trim() === normalized)) {\n  throw new Error(`Row action name \"${name}\" already in use on this table`)\n}","typeGuard":null,"tryCatchPattern":"try {\n  await rowActions.create(tableId, { name })\n} catch (e) {\n  if (e instanceof HTTPError && e.status === 409) {\n    // show 'name already exists' validation message to user\n  } else throw e\n}","preventionTips":["Enforce name uniqueness in the UI form (case-insensitive, trimmed) before submitting.","Reuse the update API for changing existing actions instead of create.","Bulk-creation scripts should dedupe names before issuing create calls."],"tags":["conflict","duplicate-name","row-actions","http-409"],"backgroundTag":"duplicate-resource-name","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}