nexu-io/open-design · error
automation template ${key} is required
Error message
automation template ${key} is required What it means
Thrown by the requiredString() helper inside normalizeAutomationTemplate. It enforces that the title, description, and purpose fields on a user automation template are present, non-empty strings. The ${key} placeholder names the offending field so the caller knows exactly which one is missing.
Source
Thrown at apps/daemon/src/automation-templates.ts:188
'off',
'balanced',
'aggressive',
]);
function storePath(dataDir: string): string {
return path.join(dataDir, STORE_DIR, STORE_FILE);
}
function cleanId(value: unknown): string {
if (typeof value !== 'string') return '';
const id = value.trim();
return SAFE_ID.test(id) ? id : '';
}
function requiredString(input: Record<string, unknown>, key: string): string {
const value = input[key];
if (typeof value !== 'string' || !value.trim()) {
throw new Error(`automation template ${key} is required`);
}
return value.trim();
}
function cleanStringArray(value: unknown): string[] {
if (!Array.isArray(value)) return [];
const out: string[] = [];
for (const item of value) {
if (typeof item !== 'string') continue;
const trimmed = item.trim();
if (trimmed && !out.includes(trimmed)) out.push(trimmed);
}
return out;
}
function cleanEnumArray<T extends string>(
value: unknown,
allowed: ReadonlySet<T>,View on GitHub (pinned to 5be4028344)
Solutions
- Add the field named in ${key} as a non-empty trimmed string to the template payload.
- Run the payload through a schema/validator (Zod or a JSON schema) before calling upsertUserAutomationTemplate so all required keys are checked up front.
- If any field is intentionally blank in your UI, default it to a sensible human-readable sentence instead of submitting an empty string.
Example fix
// before
await upsertUserAutomationTemplate(dataDir, {
id: 'my-template',
title: 'My Template',
stages: [{ id: 'ingest', kind: 'ingest', title: 'Capture' }],
// description and purpose missing -> throws "automation template description is required"
});
// after
await upsertUserAutomationTemplate(dataDir, {
id: 'my-template',
title: 'My Template',
description: 'One-line summary of what this template does.',
purpose: 'Why this template exists and when to use it.',
stages: [{ id: 'ingest', kind: 'ingest', title: 'Capture' }],
}); Defensive patterns
Strategy: validation
Validate before calling
const REQUIRED_TEMPLATE_FIELDS = ['title', 'description', 'purpose'] as const;
function validateRequiredTemplateFields(input: unknown): string | null {
if (!input || typeof input !== 'object') return 'template must be an object';
const obj = input as Record<string, unknown>;
for (const key of REQUIRED_TEMPLATE_FIELDS) {
const v = obj[key];
if (typeof v !== 'string' || !v.trim()) return key;
}
return null;
}
// before upsert:
const missing = validateRequiredTemplateFields(payload);
if (missing) throw new Error(`Missing required field: ${missing}`); Type guard
function isAutomationTemplateInput(v: unknown): v is Record<string, unknown> & { title: string; description: string; purpose: string } {
if (!v || typeof v !== 'object' || Array.isArray(v)) return false;
const o = v as Record<string, unknown>;
return ['title', 'description', 'purpose'].every(k => typeof o[k] === 'string' && (o[k] as string).trim().length > 0);
} Try / catch
try {
await upsertUserAutomationTemplate(dataDir, payload);
} catch (err) {
if (err instanceof Error && err.message.startsWith('automation template ') && err.message.endsWith(' is required')) {
// surface the missing field name back to the user/UI form
return badRequest(err.message);
}
throw err;
} Prevention
- Always include title, description, and purpose in template payloads — all three are mandatory.
- Validate with a schema (Zod or JSON schema) at the route boundary before reaching the daemon module.
- Treat whitespace-only strings as missing; the helper trims before checking.
When it happens
Trigger: Calling upsertUserAutomationTemplate(dataDir, input) (or normalizeAutomationTemplate(input)) where input.title, input.description, or input.purpose is omitted, set to an empty string, whitespace-only, or a non-string type.
Common situations: Importing an exported template JSON that omitted purpose; a UI form that does not require all three fields; a YAML-to-JSON conversion that dropped a blank field; passing a partial patch object instead of a full template.
Related errors
- sourceKind must be one of upload, url, repo, connector, arti
- ingestion body is required
- bodyMarkdown is required
- proposal body is required
- proposal title is required
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/96d054e04c845d22.
Report an issue: GitHub.