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

  1. Add the field named in ${key} as a non-empty trimmed string to the template payload.
  2. Run the payload through a schema/validator (Zod or a JSON schema) before calling upsertUserAutomationTemplate so all required keys are checked up front.
  3. 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

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


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/96d054e04c845d22. Report an issue: GitHub.