nexu-io/open-design · error

cannot overwrite built-in automation template ${template.id}

Error message

cannot overwrite built-in automation template ${template.id}

What it means

Thrown by upsertUserAutomationTemplate when the normalized template id collides with one of the six built-in templates: ingest-source-memory-tree, extract-design-system, crystallize-run-into-skill, connector-digest-design-context, compress-project-context, promote-artifact-style. Built-ins are curated and cannot be overwritten through the user upsert path.

Source

Thrown at apps/daemon/src/automation-templates.ts:328

    ...BUILT_IN_AUTOMATION_TEMPLATES,
    ...userTemplates.filter((template) => !builtInIds.has(template.id)),
  ];
}

export async function getAnyAutomationTemplate(
  dataDir: string,
  id: string,
): Promise<AutomationTemplate | null> {
  return (await listAllAutomationTemplates(dataDir)).find((template) => template.id === id) ?? null;
}

export async function upsertUserAutomationTemplate(
  dataDir: string,
  input: unknown,
): Promise<AutomationTemplate> {
  const template = normalizeAutomationTemplate(input);
  if (BUILT_IN_AUTOMATION_TEMPLATES.some((builtIn) => builtIn.id === template.id)) {
    throw new Error(`cannot overwrite built-in automation template ${template.id}`);
  }
  const current = await readUserAutomationTemplates(dataDir);
  const next = current.filter((existing) => existing.id !== template.id);
  next.push(template);
  next.sort((a, b) => a.id.localeCompare(b.id));
  await writeUserAutomationTemplates(dataDir, next);
  return template;
}

View on GitHub (pinned to 5be4028344)

Solutions

  1. Rename the id to something unique, e.g. add a suffix ('extract-design-system-v2', 'myteam:compress-context').
  2. Filter the payload against BUILT_IN_AUTOMATION_TEMPLATES (exported list) before upserting in bulk.
  3. If you genuinely need to change built-in behavior, that requires a code change to automation-templates.ts, not an upsert.

Example fix

// before
await upsertUserAutomationTemplate(dataDir, {
  id: 'extract-design-system', // collides with built-in -> throws
  /* ... */
});

// after
await upsertUserAutomationTemplate(dataDir, {
  id: 'extract-design-system-custom',
  /* ... */
});
Defensive patterns

Strategy: validation

Validate before calling

import { BUILT_IN_AUTOMATION_TEMPLATES } from './automation-templates.js';
const BUILT_IN_IDS = new Set(BUILT_IN_AUTOMATION_TEMPLATES.map(t => t.id));
if (BUILT_IN_IDS.has(payload.id)) {
  throw new Error(`Refusing to overwrite built-in template '${payload.id}'. Rename the id.`);
}

Type guard

function isUserTemplateId(id: string, builtInIds: Set<string>): boolean {
  return !builtInIds.has(id);
}

Try / catch

try {
  await upsertUserAutomationTemplate(dataDir, payload);
} catch (err) {
  if (err instanceof Error && err.message.startsWith('cannot overwrite built-in automation template ')) {
    return conflict('Pick a different id; built-in templates cannot be overwritten.');
  }
  throw err;
}

Prevention

When it happens

Trigger: POSTing a user template whose id exactly matches a built-in; forking a built-in to customize it without renaming; re-importing an export that included built-in templates alongside user ones.

Common situations: An export/import flow that round-trips the full list (built-ins plus user templates) and tries to write the built-ins back; a user copying a built-in as a starting point and forgetting to rename.

Related errors


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