nexu-io/open-design · error · Error

brand.json failed validation: ${errorMessage(err)}

Error message

brand.json failed validation: ${errorMessage(err)}

What it means

Thrown by finalizeBrand when validateBrand(parsed, meta.sourceUrl) raises. The file parsed successfully (or was extracted from a fence) but its shape does not match the Brand schema (required keys missing, wrong types such as colors not being an array or typography not an object, bad color hex values, etc.). The wrapped errorMessage(err) names the specific failure.

Source

Thrown at apps/daemon/src/brands/index.ts:1354

  const brandJsonRaw = await readProjectTextOrNull(projectsRoot, projectId, 'brand.json');
  if (brandJsonRaw === null) {
    throw new Error(
      'brand.json not found in the extraction project — the agent has not written the design system yet.',
    );
  }
  let parsed: unknown;
  try {
    parsed = JSON.parse(brandJsonRaw);
  } catch {
    const block = extractJsonBlock(brandJsonRaw);
    if (block === null) throw new Error('brand.json is not valid JSON.');
    parsed = block;
  }
  let brand: Brand;
  try {
    brand = validateBrand(parsed, meta.sourceUrl);
  } catch (err) {
    throw new Error(`brand.json failed validation: ${errorMessage(err)}`);
  }

  // Pull the agent's downloaded assets into the brand workspace so the
  // deterministic builder and the design system see them.
  copyProjectDirToBrand(projectsRoot, projectId, brandsRoot, id, 'logos');
  copyProjectDirToBrand(projectsRoot, projectId, brandsRoot, id, 'fonts');
  copyProjectDirToBrand(projectsRoot, projectId, brandsRoot, id, 'imagery');

  const guideMd =
    (await readProjectTextOrNull(projectsRoot, projectId, 'BRAND.md')) ?? brandGuideMd(brand);

  return finalizeBrandCore({ ...opts, id, projectId, meta, brand, guideMd });
}

interface FinalizeBrandCoreOptions extends FinalizeBrandOptions {
  /** Backing project to sync the finalized design system into. */
  projectId: string;
  /** Lifecycle record (already loaded by the caller). */

View on GitHub (pinned to 5be4028344)

Solutions

  1. Read errorMessage(err) in the thrown message to identify the specific field, then fix that field in brand.json.
  2. Re-run the extraction with a stricter prompt that emphasizes the required Brand schema.
  3. If you constructed the JSON by hand, compare against a known-good Brand example before finalizing.
Defensive patterns

Strategy: try-catch

Validate before calling

// Reuse the same validator the daemon uses, before calling finalizeBrand.
let parsed: unknown;
try { parsed = JSON.parse(rawBrandJson); } catch { parsed = extractJsonBlock(rawBrandJson); }
if (parsed !== null) {
  try { validateBrand(parsed, sourceUrl); }
  catch (err) { throw new Error(`Brand JSON would fail validation: ${errorMessage(err)}`); }
}

Try / catch

try {
  await finalizeBrand(opts);
} catch (err) {
  if (err instanceof Error && err.message.startsWith('brand.json failed validation: ')) {
    const detail = err.message.slice('brand.json failed validation: '.length);
    return unprocessableEntity(`Fix brand.json: ${detail}`);
  }
  throw err;
}

Prevention

When it happens

Trigger: Required keys missing (no colors, no typography); colors entries missing hex; typography not an object; arrays where objects are expected; string fields that fail per-field validators.

Common situations: Agent produced partially-shaped JSON (filled some modules, left others malformed); schema version drift between the agent's prompt and the validator; manual edit that broke the shape.

Related errors


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