nexu-io/open-design · error · Error
invalid brand id: ${brandId}
Error message
invalid brand id: ${brandId} What it means
Thrown by syncBrandSystemToUserDesignSystem when resolveBrandFile returns null for the brandId. The brand id must match /^[a-z0-9][a-z0-9-]*$/ and must not contain '..', else the traversal-safe resolver refuses it. This guards the brand -> user-design-system copy path so a stray id can never escape the brands root.
Source
Thrown at apps/daemon/src/brands/index.ts:2168
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);
if (!/^[a-z0-9][a-z0-9-]*$/u.test(dirId)) return null;
const base = path.resolve(root);View on GitHub (pinned to 5be4028344)
Solutions
- Resolve the brand id from listBrandIds(brandsRoot) or newBrandId(sourceUrl) instead of constructing it by hand.
- Validate the id with isValidBrandId (exported from store.ts) before calling the sync route, and 404 the client on failure.
- Confirm the brand dir actually exists (readBrand(brandsRoot, id) is non-null) before invoking sync.
- If a stale design-system id is the real issue, regenerate it from the brands list rather than retrying the same string.
Example fix
// before
syncBrandSystemToUserDesignSystem(udsRoot, dsId, brandsRoot, 'My_Brand', md);
// after
import { isValidBrandId } from './store.js';
if (!isValidBrandId(brandId)) throw new HttpError(404, `no such brand: ${brandId}`);
syncBrandSystemToUserDesignSystem(udsRoot, dsId, brandsRoot, brandId, md); Defensive patterns
Strategy: validation
Validate before calling
import { isValidBrandId, readBrand } from './store.js';
function assertSyncableBrandId(brandsRoot, id) {
if (!isValidBrandId(id)) throw new Error(`invalid brand id: ${id}`);
if (!readBrand(brandsRoot, id)) throw new Error(`brand not found: ${id}`);
} Type guard
import { isValidBrandId } from './store.js';
const isSyncableBrandId = (id: unknown): id is string =>
typeof id === 'string' && isValidBrandId(id); Prevention
- Mint brand ids only through newBrandId(sourceUrl); never construct them from raw input.
- Validate ids at the API boundary so the sync layer never sees an invalid one.
- Confirm readBrand is non-null before any sync/rebuild call.
When it happens
Trigger: A call to the route that copies a built brand system into a user design system (brands/index.ts:1483) with a brandId that fails isValidBrandId, e.g. 'My_Brand', 'UPPER', 'brand.json', '', '../x', or an id with slashes/spaces.
Common situations: Calling the sync endpoint directly with a hand-typed id; passing a brand.json filename instead of the dir id; corrupted/partial extraction that wrote no valid brand dir before sync was triggered; tests that synthesize ids without going through newBrandId().
Related errors
- invalid brand id: ${id}
- invalid brand id: ${input.brandId}
- invalid design system id: ${designSystemId}
- invalid brand id: ${id}
- brand is not a JSON object
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/54332d283539cc97.
Report an issue: GitHub.