{"record":{"id":"b4cb7024ec10671f","repo":"Budibase/budibase","slug":"agent-name-is-required","errorCode":null,"errorMessage":"Agent name is required.","messagePattern":"Agent name is required\\.","errorType":"http","errorClass":"HTTPError","httpStatus":400,"severity":"error","filePath":"packages/server/src/sdk/workspace/ai/agents/crud.ts","lineNumber":101,"sourceCode":"const DEFAULT_OPERATION_NAME = \"Main operation\"\nconst LEGACY_QUERY_TOOL_REPLACEMENTS_CACHE_TTL_SECONDS = 60\n\nconst fetchRaw = async (): Promise<DeprecatedAgent[]> => {\n  const db = context.getWorkspaceDB()\n  const result = await db.allDocs<DeprecatedAgent>(\n    docIds.getDocParams(DocumentType.AGENT, undefined, {\n      include_docs: true,\n    })\n  )\n\n  return result.rows\n    .map(row => row.doc)\n    .filter((doc): doc is DeprecatedAgent => !!doc)\n}\n\nconst guardName = async (name: string, id?: string) => {\n  if (!name.trim()) {\n    throw new HTTPError(NAME_REQUIRED_ERROR, 400)\n  }\n\n  const agents = await fetchRaw()\n  const normalizedName = helpers.normalizeForComparison(name)\n  const duplicate = agents.find(\n    agent =>\n      helpers.normalizeForComparison(agent.name) === normalizedName &&\n      agent._id !== id\n  )\n\n  if (duplicate) {\n    throw new HTTPError(`Agent with name '${name}' already exists.`, 400)\n  }\n}\n\nconst encodeSecret = (value?: string): string | undefined => {\n  if (!value || value.startsWith(SECRET_ENCODING_PREFIX)) {\n    return value","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/sdk/workspace/ai/agents/crud.ts#L83-L119","documentation":"guardName is the shared validation used by agent create and update. It first requires a non-empty (after trim) name, throwing HTTPError(NAME_REQUIRED_ERROR, 400) if the name is blank. Names are then checked for duplicates against existing agents using normalized comparison.","triggerScenarios":"POST/PUT to the agent CRUD API with name = \"\", \"   \", undefined coerced to empty, or a body where name is present but whitespace only; programmatic sdk.ai.agents.create/update calls that omit or blank the name field.","commonSituations":"Builder form submitting before the name input is filled; API clients sending partial update bodies with name: \"\"; trimming inconsistencies where the UI shows a name but raw value is spaces; copy-paste of whitespace-only names.","solutions":["Validate the name on the client (trim + non-empty) before calling create/update","Send a meaningful name in the request body; do not pass name: \"\" for partial updates — omit fields you don't intend to change only if the endpoint supports it, otherwise supply the existing name","Check form state binding so an unfilled input is blocked at submit time","Catch the 400 HTTPError in callers and surface a friendly 'name required' message"],"exampleFix":"// before\nawait sdk.ai.agents.update({ _id, _rev, name: \"\" })\n// after\nconst name = newName.trim()\nif (!name) throw new Error(\"Provide an agent name\")\nawait sdk.ai.agents.update({ _id, _rev, name })","handlingStrategy":"validation","validationCode":"if (typeof name !== \"string\" || !name.trim()) {\n  throw new Error(\"Agent name is required\")\n}","typeGuard":null,"tryCatchPattern":"try {\n  await sdk.ai.agents.create({ name })\n} catch (err) {\n  if (err instanceof HTTPError && err.status === 400) {\n    // surface 'name required' to the user / fix the payload\n  } else { throw err }\n}","preventionTips":["Trim and validate names in the UI before submit","Never send empty strings for untouched fields","Block form submission until a name is provided"],"tags":["validation","http-400","crud"],"backgroundTag":"missing-required-field","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}