nexu-io/open-design · error · Error

brand.json is not valid JSON.

Error message

brand.json is not valid JSON.

What it means

Thrown by finalizeBrand when JSON.parse(brandJsonRaw) failed AND extractJsonBlock(brandJsonRaw) could not find a fenced code block containing JSON. The file exists and has content, but it is neither parseable JSON nor a markdown fence the lenient extractor can recover. Distinct from error 34 (file absent) and error 36 (parses but fails schema).

Solutions

  1. Open the project's brand.json and inspect the contents to see what the agent actually wrote.
  2. Re-run the extraction so the agent writes a clean JSON file.
  3. If the content is JSON wrapped in prose, wrap it in a ```json fence so extractJsonBlock can recover it (the lenient path).
Defensive patterns

Strategy: validation

Validate before calling

function looksLikeJsonOrFence(raw: string): boolean {
  const trimmed = raw.trim();
  if (!trimmed) return false;
  try { JSON.parse(trimmed); return true; } catch { /* fall through */ }
  return /```(?:json)?[\s\S]*?```/i.test(trimmed);
}
const raw = await readProjectTextOrNull(projectsRoot, projectId, 'brand.json');
if (raw !== null && !looksLikeJsonOrFence(raw)) {
  throw new Error('brand.json is neither valid JSON nor a fenced JSON block. Re-run the extraction.');
}

Try / catch

try {
  await finalizeBrand(opts);
} catch (err) {
  if (err instanceof Error && err.message === 'brand.json is not valid JSON.') {
    return conflict('brand.json contents are malformed. Inspect the file or re-run the extraction.');
  }
  throw err;
}

Prevention

When it happens

Trigger: The agent wrote prose/markdown without a ```json fence; wrote truncated or partially-streamed JSON; wrote YAML or another format; the file was corrupted on write.

Common situations: Agent interrupted mid-write (partial flush); model returned commentary instead of structured output; concurrent writes or disk-full truncation.

Related errors


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

Appendix: source

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

  opts: FinalizeBrandOptions,
): Promise<BrandFinalizeResponse> {
  const { id, brandsRoot, projectsRoot } = opts;
  const meta = readMeta(brandsRoot, id);
  if (!meta) throw new Error(`brand not found: ${id}`);
  const projectId = opts.projectId ?? meta.projectId ?? brandProjectId(id);

  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);

View on GitHub (pinned to 5be4028344)