nexu-io/open-design · error · Error
invalid design system id: ${designSystemId}
Error message
invalid design system id: ${designSystemId} What it means
Thrown by syncBrandSystemToUserDesignSystem when userDesignSystemDir(root, designSystemId) returns null. The id must start with the literal prefix 'user:', the slug after the prefix must match /^[a-z0-9][a-z0-9-]*$/ (lowercase alphanumeric + hyphens, starting alphanumeric), AND the resolved path must stay inside root (path-containment guard). Built-in ids without the 'user:' prefix are rejected here.
Source
Thrown at apps/daemon/src/brands/index.ts:2166
metadata: ProjectMetadata,
sourceDir: string,
targetPrefix: string,
): Promise<void> {
for (const file of collectFiles(sourceDir)) {
const projectPath = toPosixPath(path.join(targetPrefix, file.rel));
await writeProjectFile(projectsRoot, projectId, projectPath, fs.readFileSync(file.abs), { overwrite: true }, metadata);
}
}
function syncBrandSystemToUserDesignSystem(
userDesignSystemsRoot: string,
designSystemId: string,
brandsRoot: string,
brandId: string,
designMd: string,
): void {
const dir = userDesignSystemDir(userDesignSystemsRoot, designSystemId);
if (!dir) throw new Error(`invalid design system id: ${designSystemId}`);
const brandRoot = resolveBrandFile(brandsRoot, brandId, []);
if (!brandRoot) throw new Error(`invalid brand id: ${brandId}`);
fs.writeFileSync(path.join(dir, 'DESIGN.md'), designMd, 'utf8');
copyDirectorySync(brandSystemDir(brandsRoot, brandId), path.join(dir, 'system'));
copyOptionalDirectorySync(path.join(brandRoot, 'logos'), path.join(dir, 'logos'));
copyOptionalDirectorySync(path.join(brandRoot, 'fonts'), path.join(dir, 'fonts'));
copyOptionalDirectorySync(path.join(brandRoot, 'imagery'), path.join(dir, 'imagery'));
copyOptionalDirectorySync(path.join(brandRoot, 'prefetch'), path.join(dir, 'prefetch'));
const brandJson = resolveBrandFile(brandsRoot, brandId, ['brand.json']);
if (brandJson && isFile(brandJson)) {
fs.copyFileSync(brandJson, path.join(dir, 'brand.json'));
}
}
function userDesignSystemDir(root: string, id: string): string | null {
if (!id.startsWith('user:')) return null;
const dirId = id.slice('user:'.length);View on GitHub (pinned to 5be4028344)
Solutions
- Prefix the id with 'user:' (e.g. 'user:my-brand').
- Ensure the slug after the prefix is lowercase, starts with an alphanumeric, and uses only [a-z0-9-].
- Reject ids with '/', '\', '..', or uppercase before calling this function.
Example fix
// before syncBrandSystemToUserDesignSystem(root, 'My-Brand', brandsRoot, brandId, designMd); // missing user: prefix + uppercase -> throws // after syncBrandSystemToUserDesignSystem(root, 'user:my-brand', brandsRoot, brandId, designMd);
Defensive patterns
Strategy: validation
Validate before calling
function isUserDesignSystemId(id: string): boolean {
if (!id.startsWith('user:')) return false;
const slug = id.slice('user:'.length);
return /^[a-z0-9][a-z0-9-]*$/u.test(slug);
}
if (!isUserDesignSystemId(designSystemId)) {
throw new Error(`Design system id must start with 'user:' and use a lowercase kebab slug.`);
} Type guard
function isUserDesignSystemId(id: unknown): id is string {
return typeof id === 'string'
&& id.startsWith('user:')
&& /^[a-z0-9][a-z0-9-]*$/u.test(id.slice('user:'.length));
} Try / catch
try {
syncBrandSystemToUserDesignSystem(root, designSystemId, brandsRoot, brandId, designMd);
} catch (err) {
if (err instanceof Error && err.message.startsWith('invalid design system id:')) {
return badRequest("Design system id must be 'user:<lowercase-kebab-slug>'.");
}
throw err;
} Prevention
- Always prefix user design-system ids with 'user:'.
- Lowercase and kebab-case the slug portion; only [a-z0-9-] are allowed after the first alphanumeric.
- Reject ids with slashes, dots, underscores, or uppercase before calling the sync function.
When it happens
Trigger: Passing a design-system id that omits the 'user:' prefix (e.g. a built-in id); an id with uppercase, underscores, dots, or slashes; an id whose resolved path escapes the user design systems root.
Common situations: Using a raw slug instead of the prefixed form; passing a built-in design-system id where a user one is required; constructing ids from untrusted input without the prefix or charset check.
Related errors
- invalid brand id: ${input.brandId}
- --image path "${rel}" resolves outside the project directory
- invalid memory id
- path traversal detected in input_images for job {job.get('id
- path traversal detected in prompt_file for job {job_id}
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/777e75050e99c06a.
Report an issue: GitHub.