nexu-io/open-design · error · Error

brand: missing required `name`

Error message

brand: missing required `name`

What it means

Thrown by validateBrand when the object has no non-empty string `name` field. name is the only hard-required scalar on a Brand — without it, kit pages, deck titles, and meta cannot render. The check is `typeof o.name === 'string' && o.name.trim().length > 0`.

Source

Thrown at apps/daemon/src/brands/validate.ts:55

  return {
    family: isStr(o.family) && o.family ? o.family : fallbackFamily,
    fallbacks: strArr(o.fallbacks),
    weights: Array.isArray(o.weights) ? o.weights.filter((w) => typeof w === 'number') : [400, 700],
    ...(isStr(o.googleFontsUrl) && o.googleFontsUrl ? { googleFontsUrl: o.googleFontsUrl } : {}),
    ...(isStr(o.notes) && o.notes ? { notes: o.notes } : {}),
  };
}

/**
 * Validate + normalize a brand object. Throws with a precise message on
 * unrecoverable problems (missing name/colors); fills sensible defaults for
 * everything optional so a slightly-sloppy input still renders.
 */
export function validateBrand(raw: unknown, sourceUrl: string): Brand {
  if (!raw || typeof raw !== 'object') throw new Error('brand is not a JSON object');
  const o = raw as Record<string, unknown>;

  if (!isStr(o.name) || !o.name.trim()) throw new Error('brand: missing required `name`');

  const rawColors = Array.isArray(o.colors) ? o.colors : [];
  const colors: BrandColor[] = [];
  for (const c of rawColors) {
    if (!c || typeof c !== 'object') continue;
    const co = c as Record<string, unknown>;
    const role = BRAND_COLOR_ROLES.includes(co.role as BrandColorRole)
      ? (co.role as BrandColorRole)
      : null;
    const hex = isStr(co.hex) && /^#[0-9a-fA-F]{6}$/.test(co.hex) ? co.hex.toLowerCase() : null;
    if (!role || !hex) continue;
    colors.push({
      role,
      hex,
      oklch: isStr(co.oklch) ? co.oklch : '',
      name: isStr(co.name) ? co.name : role,
      usage: isStr(co.usage) ? co.usage : '',
    });

View on GitHub (pinned to 5be4028344)

Solutions

  1. Alias common synonyms to `name` before validateBrand: if (!o.name && o.brandName) o.name = o.brandName;
  2. Improve the extraction prompt to require `name` as a non-empty string.
  3. Re-prompt the agent with this exact error message when extraction omits name.
  4. Add a pre-check that fails fast with a clearer message for the synonym case.

Example fix

// before
const o = raw as Record<string, unknown>;
const brand = validateBrand(o, sourceUrl);
// after — normalize synonyms before validation
if (!o.name) o.name = o.brandName ?? o.title ?? o.company;
const brand = validateBrand(o, sourceUrl);
Defensive patterns

Strategy: validation

Validate before calling

if (!o.name) o.name = o.brandName ?? o.title ?? o.company;
if (typeof o.name !== 'string' || !o.name.trim()) {
  throw new Error('brand: missing required `name`');
}

Type guard

const hasBrandName = (o: Record<string, unknown>): boolean =>
  typeof o.name === 'string' && o.name.trim().length > 0;

Prevention

When it happens

Trigger: validateBrand receives an object whose `name` is missing, empty, whitespace-only, or not a string (a number, null, array). Common when the extractor emits a brand with a `brandName` or `title` field instead of `name`.

Common situations: LLM uses a synonym ('brandName', 'title', 'company') instead of `name`; name field left blank; name field set to a number (e.g. a founding year) by a sloppy extraction; an upstream rename that did not migrate old brand.json.

Related errors


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