{"record":{"id":"ea4159d311acf575","repo":"different-ai/openwork","slug":"invalid-command-template","errorCode":"invalid_command_template","errorMessage":"Command template is required","messagePattern":"Command template is required","errorType":"validation","errorClass":"ApiError","httpStatus":400,"severity":"error","filePath":"apps/server/src/commands.ts","lineNumber":80,"sourceCode":"  if (scope === \"global\") {\n    const dir = join(homedir(), \".config\", \"opencode\", \"commands\");\n    return listCommandsInDir(dir, \"global\");\n  }\n  return listCommandsInDir(projectCommandsDir(workspaceRoot), \"workspace\");\n}\n\nexport type UpsertCommandPayload = {\n  name: string;\n  description?: string;\n  template: string;\n  agent?: string;\n  model?: string | null;\n  subtask?: boolean;\n};\n\nexport function buildCommandContent(payload: UpsertCommandPayload): { name: string; content: string } {\n  if (!payload.template || payload.template.trim().length === 0) {\n    throw new ApiError(400, \"invalid_command_template\", \"Command template is required\");\n  }\n  const sanitized = sanitizeCommandName(payload.name);\n  validateCommandName(sanitized);\n  const frontmatter = buildFrontmatter(normalizeCommandFrontmatter({\n    name: sanitized,\n    description: payload.description,\n    agent: payload.agent,\n    model: payload.model,\n    subtask: payload.subtask ?? false,\n  }));\n  const content = frontmatter + \"\\n\" + payload.template.trim() + \"\\n\";\n  return { name: sanitized, content };\n}\n\nexport async function upsertCommand(\n  workspaceRoot: string,\n  payload: UpsertCommandPayload,\n): Promise<string> {","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/apps/server/src/commands.ts#L62-L98","documentation":"buildCommandContent validates that an upsert-command payload includes a non-empty template before writing the command's markdown frontmatter. An empty or whitespace-only template is rejected with a 400 ApiError.","triggerScenarios":"Calling the command upsert API (or buildCommandContent directly) with payload.template omitted, null, or set to \"\" / \"   \".","commonSituations":"Client form submitted without filling the prompt body; API client sends only name/description; older clients predating a template-required schema change; JSON body missing the field due to serialization bugs.","solutions":["Include a non-empty template string in the payload before calling the upsert endpoint","Trim-check the template on the client before submitting and show a form validation error","If migrating from an older API version, add the template field that is now required"],"exampleFix":"// before\nawait api.upsertCommand({ name: \"deploy\", description: \"Deploy the app\" });\n// after\nawait api.upsertCommand({ name: \"deploy\", description: \"Deploy the app\", template: \"Run the deploy pipeline for $ARGUMENTS\" });","handlingStrategy":"validation","validationCode":"const parsed = UpsertSchema.safeParse(payload); if (!parsed.success) throw new Error(parsed.error.issues[0].message);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Share one Zod schema between client and server","Trim template input before submit","Show required-field errors in the form UI"],"tags":["validation","http-400","commands"],"backgroundTag":"missing-required-argument","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}