nexu-io/open-design · error · Error
invalid brand id: ${id}
Error message
invalid brand id: ${id} What it means
Thrown by createBrandDir when the id fails isValidBrandId. This is the very first write of a brand's lifecycle (mkdir + initial meta.json), so an invalid id here means the brand dir was never created. The id must match /^[a-z0-9][a-z0-9-]*$/ and contain no '..'.
Source
Thrown at apps/daemon/src/brands/store.ts:83
* Resolve `relParts` under a brand dir with a traversal guard. Returns the
* absolute path, or null when the id is invalid or the resolved path escapes
* the brand dir.
*/
export function resolveBrandFile(
brandsRoot: string,
id: string,
relParts: string[],
): string | null {
if (!isValidBrandId(id)) return null;
const base = path.resolve(brandDir(brandsRoot, id));
const target = path.resolve(base, ...relParts);
if (target !== base && !target.startsWith(`${base}${path.sep}`)) return null;
return target;
}
/** Create the brand dir and write its initial meta.json. */
export function createBrandDir(brandsRoot: string, id: string, meta: BrandMeta): void {
if (!isValidBrandId(id)) throw new Error(`invalid brand id: ${id}`);
fs.mkdirSync(brandDir(brandsRoot, id), { recursive: true });
writeMeta(brandsRoot, id, meta);
}
function readJson<T>(file: string): T | null {
try {
return JSON.parse(fs.readFileSync(file, 'utf8')) as T;
} catch {
return null;
}
}
function writeJson(file: string, value: unknown): void {
fs.mkdirSync(path.dirname(file), { recursive: true });
fs.writeFileSync(file, JSON.stringify(value, null, 2), 'utf8');
}
export function readMeta(brandsRoot: string, id: string): BrandMeta | null {View on GitHub (pinned to 5be4028344)
Solutions
- Always mint ids via newBrandId(sourceUrl) (host slug + 6-char random suffix); never accept caller-supplied ids for creation.
- At the API boundary, run isValidBrandId(id) and 400 the request on failure.
- If you need a deterministic id for tests, use a lowercase-hyphen slug that matches the regex (e.g. 'test-brand-1').
- Add a unit test over createBrandDir with a table of invalid ids to lock the contract.
Example fix
// before
createBrandDir(brandsRoot, req.body.id, meta);
// after
import { isValidBrandId, newBrandId } from './store.js';
const id = req.body.id ? validateAndNormalize(req.body.id) : newBrandId(meta.sourceUrl);
if (!isValidBrandId(id)) return res.status(400).json({ error: 'invalid brand id' });
createBrandDir(brandsRoot, id, meta); Defensive patterns
Strategy: validation
Validate before calling
import { isValidBrandId, newBrandId } from './store.js';
const id = newBrandId(sourceUrl);
if (!isValidBrandId(id)) throw new Error('internal: generated invalid id'); Type guard
import { isValidBrandId } from './store.js';
const isBrandId = (id: unknown): id is string =>
typeof id === 'string' && isValidBrandId(id); Prevention
- Always mint ids with newBrandId; never accept caller-supplied ids for creation.
- Validate ids at the API boundary.
- Lock the contract with a unit test over a table of invalid ids.
When it happens
Trigger: createBrandDir is invoked with a caller-supplied id that contains uppercase, underscores, slashes, dots, spaces, is empty, or contains '..'. Typically the caller should be using newBrandId(sourceUrl) to mint the id instead.
Common situations: Code path that takes a brand id from request body or CLI arg and passes it straight to createBrandDir; tests that hard-code ids like 'Test_Brand'; a refactor that replaced newBrandId with a hand-built slug; race where the id is computed from untrusted input.
Related errors
- invalid brand id: ${brandId}
- invalid brand id: ${id}
- brand.json not found for brand "${id}"
- brand is not a JSON object
- brand: missing required `name`
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/cd0691bc05c58d34.
Report an issue: GitHub.