nexu-io/open-design · error
automation template id must be a safe slug
Error message
automation template id must be a safe slug
What it means
Thrown when cleanId(raw.id) returns empty, i.e. raw.id fails SAFE_ID = /^[a-z0-9][a-z0-9._-]{1,95}$/. The id must be lowercase, start with an alphanumeric character, be 2-96 characters long, and contain only [a-z0-9._-]. Uppercase, spaces, slashes, leading punctuation, and over-length ids are rejected.
Source
Thrown at apps/daemon/src/automation-templates.ts:237
function cleanReviewPolicy(value: unknown): AutomationReviewPolicy {
return typeof value === 'string' && REVIEW_POLICIES.has(value as AutomationReviewPolicy)
? value as AutomationReviewPolicy
: 'always';
}
function cleanCompressionMode(value: unknown): AutomationTokenCompressionMode {
return typeof value === 'string' && COMPRESSION_MODES.has(value as AutomationTokenCompressionMode)
? value as AutomationTokenCompressionMode
: 'balanced';
}
export function normalizeAutomationTemplate(input: unknown): AutomationTemplate {
if (!input || typeof input !== 'object' || Array.isArray(input)) {
throw new Error('automation template must be an object');
}
const raw = input as Record<string, unknown>;
const id = cleanId(raw.id);
if (!id) throw new Error('automation template id must be a safe slug');
const rawStages = Array.isArray(raw.stages) ? raw.stages : [];
const stages = rawStages
.map((stage): AutomationTemplate['stages'][number] | null => {
if (!stage || typeof stage !== 'object' || Array.isArray(stage)) return null;
const stageRaw = stage as Record<string, unknown>;
const stageId = cleanId(stageRaw.id);
const kind = stageRaw.kind;
const title = stageRaw.title;
if (!stageId || typeof title !== 'string' || !title.trim()) return null;
if (typeof kind !== 'string' || !STAGE_KINDS.has(kind as AutomationTemplateStageKind)) {
return null;
}
return {
id: stageId,
kind: kind as AutomationTemplateStageKind,
title: title.trim(),
...(typeof stageRaw.description === 'string' && stageRaw.description.trim()
? { description: stageRaw.description.trim() }View on GitHub (pinned to 5be4028344)
Solutions
- Slugify the id to lowercase kebab-case (e.g. 'my-template-v2').
- Ensure the first character is a letter or digit and total length is between 2 and 96.
- Strip spaces, slashes, and uppercase before submitting; only [a-z0-9._-] are allowed after the first char.
Example fix
// before
await upsertUserAutomationTemplate(dataDir, { id: 'My Template!', /* ... */ });
// after
await upsertUserAutomationTemplate(dataDir, { id: 'my-template', /* ... */ }); Defensive patterns
Strategy: validation
Validate before calling
const SAFE_ID = /^[a-z0-9][a-z0-9._-]{1,95}$/;
function isValidTemplateId(id: unknown): id is string {
return typeof id === 'string' && SAFE_ID.test(id);
}
if (!isValidTemplateId(payload.id)) {
throw new Error('id must be lowercase, 2-96 chars, start alphanumeric, only [a-z0-9._-]');
} Type guard
const SAFE_ID = /^[a-z0-9][a-z0-9._-]{1,95}$/;
function isSafeTemplateId(id: unknown): id is string {
return typeof id === 'string' && SAFE_ID.test(id);
} Try / catch
try {
await upsertUserAutomationTemplate(dataDir, payload);
} catch (err) {
if (err instanceof Error && err.message === 'automation template id must be a safe slug') {
return badRequest('Template id must be a lowercase slug (2-96 chars, [a-z0-9._-]).');
}
throw err;
} Prevention
- Always slugify user-entered ids to lowercase kebab-case.
- Cap id length at 96 characters and require an alphanumeric first character.
- Run SAFE_ID.test(id) on the client before submitting.
When it happens
Trigger: Submitting a template whose id contains uppercase letters, spaces, or slashes (e.g. 'My Template' or 'foo/bar'); an id with a leading underscore or dot ('.foo', '_bar'); an id longer than 96 characters; a missing id field.
Common situations: Using a human title as the id; slugify producing Title Case or leaving spaces; auto-generated ids that include UUIDs with uppercase hex (UUIDs are lowercase hex+dash and pass, but any uppercase variant fails); copy-pasting an id with a trailing newline.
Related errors
- automation template ${key} is required
- automation template must be an object
- automation template requires at least one valid stage
- sourceKind must be one of upload, url, repo, connector, arti
- ingestion body is required
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/c8b00fb8ab3e61fd.
Report an issue: GitHub.