{"record":{"id":"05d3d55ab23f6137","repo":"Budibase/budibase","slug":"template-name-must-contain-letters-or-numbers","errorCode":null,"errorMessage":"Template name must contain letters or numbers","messagePattern":"Template name must contain letters or numbers","errorType":"http","errorClass":"HTTPError","httpStatus":400,"severity":"warning","filePath":"packages/server/src/sdk/workspace/restTemplates.ts","lineNumber":95,"sourceCode":"  return response.rows\n    .map(row => row.doc as CustomRestTemplateDocument | undefined)\n    .filter(\n      (document): document is CustomRestTemplateDocument => document != null\n    )\n    .map(toRestTemplate)\n}\n\nconst createWithoutLock = async ({\n  name,\n  description,\n  data,\n  fileExtension,\n  operationsCount,\n}: CreateCustomRestTemplateParams): Promise<RestTemplate> => {\n  const db = context.getWorkspaceDB()\n  const normalizedName = kebabCase(name)\n  if (!normalizedName) {\n    throw new HTTPError(\"Template name must contain letters or numbers\", 400)\n  }\n\n  const existingTemplate = (await fetch()).find(\n    template => kebabCase(template.name) === normalizedName\n  )\n  if (existingTemplate) {\n    throw new HTTPError(\n      `A custom REST template named \"${name.trim()}\" already exists`,\n      409\n    )\n  }\n\n  const restTemplateId = dbCore.generateRestTemplateID()\n  const existing = await db.tryGet<CustomRestTemplateDocument>(restTemplateId)\n  if (existing) {\n    throw new HTTPError(\n      `A custom REST template named \"${name.trim()}\" already exists`,\n      409","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/sdk/workspace/restTemplates.ts#L77-L113","documentation":"A 400 HTTPError thrown when creating a custom REST template whose name, after kebab-case normalization, produces an empty string. The SDK requires template names to contain at least one letter or digit so a usable identifier can be derived.","triggerScenarios":"Calling create (createWithoutLock) with name consisting only of characters stripped by kebabCase — e.g. \"---\", \"___\", spaces, or punctuation-only strings — or an empty/whitespace-only name.","commonSituations":"UI form validation gap allowing a symbol-only template name; programmatic generation producing a name of only special characters; pasting a label like \"###\" as the template name; localization where the name is entirely non-ASCII symbols.","solutions":["Provide a name containing at least one alphanumeric character before calling create","Trim the input and validate client-side that /[a-z0-9]/i matches before submitting","If the display name is symbolic, prefix it with a meaningful word (e.g. \"alpha-###\")","Return a clear field-level validation error to the user instead of submitting"],"exampleFix":"// before\nawait restTemplates.create({ name: \"---\" })\n// after\nconst name = userInput.trim()\nif (!/[a-z0-9]/i.test(name)) throw new Error(\"Name needs letters/numbers\")\nawait restTemplates.create({ name })","handlingStrategy":"validation","validationCode":"if (!/[a-z0-9]/i.test(name ?? \"\")) throw new Error(\"Template name must contain letters or numbers\")","typeGuard":"function isValidTemplateName(name: unknown): name is string\n  return typeof name === \"string\" && /[a-z0-9]/i.test(name)","tryCatchPattern":"try {\n  await restTemplates.create({ name })\n} catch (e) {\n  if (e?.status === 400 && /letters or numbers/.test(e.message)) {\n    throw new ValidationError(\"Please enter a name with letters or numbers\")\n  }\n  throw e\n}","preventionTips":["Trim and regex-check names in the UI before submitting","Reject symbol-only and whitespace-only names at form level","Avoid building names purely from user labels that may be punctuation","Document the kebab-case normalization requirement to API consumers"],"tags":["http-400","validation","input-validation","rest-templates"],"backgroundTag":"invalid-input-validation","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}