{"record":{"id":"48737b62c365f7f6","repo":"Budibase/budibase","slug":"operation-with-name-operationname-trim-alr","errorCode":null,"errorMessage":"Operation with name '${operationName?.trim()}' already exists.","messagePattern":"Operation with name '(.+?)' already exists\\.","errorType":"http","errorClass":"HTTPError","httpStatus":400,"severity":"warning","filePath":"packages/server/src/sdk/workspace/ai/agents/operations.ts","lineNumber":62,"sourceCode":"const assertUniqueOperationName = (\n  agent: Agent,\n  operationName: string | undefined,\n  excludedOperationId?: string\n) => {\n  const normalizedName = normalizeOperationName(operationName)\n  if (!normalizedName) {\n    return\n  }\n\n  const hasDuplicateName = (agent.operations ?? []).some(operation => {\n    return (\n      operation.id !== excludedOperationId &&\n      normalizeOperationName(operation.name) === normalizedName\n    )\n  })\n\n  if (hasDuplicateName) {\n    throw new HTTPError(\n      `Operation with name '${operationName?.trim()}' already exists.`,\n      400\n    )\n  }\n}\n\nexport async function createOperation(\n  agentId: string,\n  operation: CreateAgentOperationInput\n): Promise<Agent> {\n  const existing = await getOrThrow(agentId)\n  if (existing.operations?.some(candidate => candidate.id === operation.id)) {\n    throw new HTTPError(\"Operation already exists\", 400)\n  }\n  assertUniqueOperationName(existing, operation.name)\n\n  return update({\n    ...existing,","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/sdk/workspace/ai/agents/operations.ts#L44-L80","documentation":"assertUniqueOperationName enforces unique operation names per agent, case-insensitively (trimmed, lowercased). When another operation on the same agent (excluding the one being updated) normalizes to the same name, it throws a 400 HTTPError naming the conflicting name.","triggerScenarios":"createOperation with a name that collides with an existing operation on the agent, or updateOperation renaming an operation to a name already used by a sibling operation (case/whitespace differences still collide).","commonSituations":"Users renaming 'Run Report' to 'run report' which clashes with an existing 'Run Report'; bulk-importing operations with duplicate labels; API integrations generating non-unique names.","solutions":["Check the agent's existing operation names (normalized) before create/rename and pick a unique name.","If updating, ensure the name check excludes only the operation being edited - rename to something not used by others.","Catch the 400 HTTPError and show a 'name already in use' validation message to the user."],"exampleFix":"// before\nawait updateOperation(agentId, opId, { name: existingName }) // 400\n// after\nconst name = \"Run Report 2\"\nconst clash = agent.operations?.some(o => o.id !== opId && o.name?.trim().toLowerCase() === name.trim().toLowerCase())\nif (clash) throw new HTTPError(\"Name in use\", 400)\nawait updateOperation(agentId, opId, { name })","handlingStrategy":"validation","validationCode":"const norm = (n?: string) => n?.trim().toLowerCase() || \"\"\nconst isNameFree = (agent: Agent, name: string, excludeId?: string) =>\n  !(agent.operations ?? []).some(o => o.id !== excludeId && norm(o.name) === norm(name))\nif (!isNameFree(agent, newName, opId)) {\n  throw new HTTPError(\"Name already in use\", 400)\n}","typeGuard":"const isUniqueName = (agent: Agent, name: string, excludeId?: string): boolean =>\n  !(agent.operations ?? []).some(\n    o => o.id !== excludeId && o.name?.trim().toLowerCase() === name.trim().toLowerCase()\n  )","tryCatchPattern":"try {\n  await createOperation(agentId, { id, name })\n} catch (err) {\n  if (err instanceof HTTPError && err.status === 400 && err.message.includes(\"already exists\")) {\n    // prompt user for a different name\n  }\n}","preventionTips":["Enforce name uniqueness in UI forms with live validation against existing operations.","Always compare names trimmed and lowercased, matching server normalization.","In imports, auto-suffix duplicate names instead of failing."],"tags":["validation","duplicate","http-400"],"backgroundTag":"duplicate-name-conflict","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}