nexu-io/open-design · error

brand backing project not found: ${projectId}

Error message

brand backing project not found: ${projectId}

What it means

Thrown by continueBrandExtraction when the brand meta exists but getProject(db, projectId) returns null, meaning the SQLite project row backing the brand is gone. The on-disk brand survived but the project database was reset, dropped, or surgically modified. Indicates a data-integrity split between filesystem and DB.

Source

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

      extractionSettled = true;
      clearTimeout(stallTimer);
      clearTimeout(hardCapTimer);
    });
  input.onBackgroundExtraction?.(settled);

  return settled;
}

export async function continueBrandExtraction(
  opts: ContinueBrandExtractionOptions,
): Promise<StartBrandExtractionResult> {
  const detail = readBrandDetail(opts.brandsRoot, opts.id);
  if (!detail) throw new Error(`brand not found: ${opts.id}`);
  const { meta } = detail;
  const sourceUrl = meta.sourceUrl;
  const projectId = meta.projectId ?? brandProjectId(opts.id);
  const project = getProject(opts.db, projectId);
  if (!project) throw new Error(`brand backing project not found: ${projectId}`);

  const randomId = opts.randomId ?? randomUUID;
  const locale = normalizeBrandKitLocale(opts.locale ?? meta.locale);
  const hasWebsiteSource = /^https?:\/\//i.test(sourceUrl);
  const designMd = readDesignMdInput(opts.brandsRoot, opts.id);
  const hasDesignMdSource = Boolean(designMd) || sourceUrl.startsWith('designmd://');
  const host = hostnameOf(sourceUrl);
  const now = Date.now();
  const extractionAttemptId = newBrandExtractionAttemptId();
  const conversationId = resolveBrandRetryConversationId({
    db: opts.db,
    projectId,
    preferredConversationId: meta.conversationId ?? null,
    randomId,
    now,
  });

  let nextMeta = patchMeta(opts.brandsRoot, opts.id, {

View on GitHub (pinned to 5be4028344)

Solutions

  1. Restore the project database so the backing project row exists again.
  2. Re-create the backing project and update the brand meta's projectId to match.
  3. If reconciliation is not possible, re-run startBrandExtraction to create a fresh brand + project pair and delete the orphaned brand.
Defensive patterns

Strategy: validation

Validate before calling

const detail = readBrandDetail(brandsRoot, opts.id);
if (!detail) throw new NotFoundError(`brand not found: ${opts.id}`);
const projectId = detail.meta.projectId ?? brandProjectId(opts.id);
const project = getProject(db, projectId);
if (!project) {
  throw new DataIntegrityError(`Brand '${opts.id}' has no backing project row '${projectId}'. Re-create the brand or restore the project DB.`);
}

Try / catch

try {
  await continueBrandExtraction(opts);
} catch (err) {
  if (err instanceof Error && /^brand backing project not found:/.test(err.message)) {
    return conflict('Backing project is gone. Re-run startBrandExtraction to recreate it.');
  }
  throw err;
}

Prevention

When it happens

Trigger: The SQLite DB was wiped or restored from an older backup while the brands directory was left intact; someone manually deleted the project row; the project DB file was moved.

Common situations: Partial data restore (DB restored without the brands dir, or vice versa); manual DB surgery; a migration that dropped project rows but not brands.

Related errors


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