nexu-io/open-design · error · Error

invalid brand id: ${id}

Error message

invalid brand id: ${id}

What it means

Thrown by the private brandRoot helper in system.ts when resolveBrandFile(brandsRoot, id, []) returns null. brandRoot backs brandSystemDir and readFontManifest inside rebuildSystem, so any caller of rebuildSystem with an invalid id ultimately hits this. Same regex + traversal guard as the store.

Source

Thrown at apps/daemon/src/brands/system.ts:40

  type SeedToken,
  type ThemeAlgorithm,
} from './engine/index.js';
import type { AssetKind, Brand } from './schema.js';

export const BRAND_ARTIFACT_KINDS: AssetKind[] = [
  'landing',
  'deck',
  'poster',
  'email',
  'newsletter',
  'form',
];

const THEME_ALGORITHMS: ThemeAlgorithm[] = ['default', 'dark', 'compact'];

function brandRoot(brandsRoot: string, id: string): string {
  const dir = resolveBrandFile(brandsRoot, id, []);
  if (!dir) throw new Error(`invalid brand id: ${id}`);
  return dir;
}

export function brandSystemDir(brandsRoot: string, id: string): string {
  return path.join(brandRoot(brandsRoot, id), 'system');
}

function reassembleWithSeed(
  base: BrandSystem,
  brand: Brand,
  seed: SeedToken,
  fontFiles: FontFile[],
): BrandSystem {
  const themes: Record<ThemeAlgorithm, DesignTokens> = {
    default: deriveTokens(seed, 'default'),
    dark: deriveTokens(seed, 'dark'),
    compact: deriveTokens(seed, 'compact'),
  };

View on GitHub (pinned to 5be4028344)

Solutions

  1. Validate the id with isValidBrandId and confirm readBrand(brandsRoot, id) !== null before rebuildSystem.
  2. Source rebuild ids from listBrandIds output only.
  3. In the rebuild route, return 404 for a missing brand rather than letting brandRoot throw.
  4. Order checks in rebuildSystem consumers: exists -> rebuild, so brandRoot is never reached for missing ids.

Example fix

// before
await rebuildSystem(brandsRoot, id);
// after
import { isValidBrandId, readBrand } from './store.js';
if (!isValidBrandId(id) || !readBrand(brandsRoot, id)) {
  throw new HttpError(404, `brand not found: ${id}`);
}
await rebuildSystem(brandsRoot, id);
Defensive patterns

Strategy: validation

Validate before calling

import { isValidBrandId, readBrand } from './store.js';
if (!isValidBrandId(id) || !readBrand(brandsRoot, id)) {
  throw new Error(`brand not found: ${id}`);
}

Type guard

import { isValidBrandId } from './store.js';
const isBrandId = (id: unknown): id is string =>
  typeof id === 'string' && isValidBrandId(id);

Prevention

When it happens

Trigger: rebuildSystem(brandsRoot, id) invoked with an id that fails isValidBrandId. Because rebuildSystem first calls readBrand (which returns null for the same invalid id), error [51] usually fires first — but if readBrand is bypassed or the id is valid-yet-missing, brandRoot throws here instead.

Common situations: Rebuild route that accepts the id from the request path; tests that call rebuildSystem directly with a fabricated id; a stale id cached after the brand was deleted; a refactor that called brandSystemDir before readBrand.

Related errors


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