tinyhumansai/openhuman · error · Error

provider-chain source marker contained no `N. Component — ro

Error message

provider-chain source marker contained no `N. Component — role` rows

What it means

The marker block in App.tsx was found, but not a single line inside the region matched the row regex /^\s*\*?\s*(\d+)\.\s+(.+?)\s+—\s+(.+?)\s*$/. Rows MUST be `N. Component — role`, numbered, and joined to the role by a single em dash (—, U+2014). An empty parse means the block is prose-only or the dash/numbering format drifted, so the generator aborts instead of writing an empty provider table.

Source

Thrown at scripts/generate-architecture-docs.mjs:66

export function parseProviderChain(appSource) {
  const begin = appSource.indexOf('@generated-source:provider-chain');
  const end = appSource.indexOf('@end-source:provider-chain');
  if (begin === -1 || end === -1 || end < begin) {
    throw new Error(
      'provider-chain source marker not found in App.tsx; expected ' +
        '`@generated-source:provider-chain` … `@end-source:provider-chain`'
    );
  }
  const region = appSource.slice(begin, end);
  const rowRe = /^\s*\*?\s*(\d+)\.\s+(.+?)\s+—\s+(.+?)\s*$/;
  const providers = [];
  for (const line of region.split('\n')) {
    const m = rowRe.exec(line);
    if (!m) continue;
    providers.push({ order: Number(m[1]), name: m[2].trim(), role: m[3].trim() });
  }
  if (providers.length === 0) {
    throw new Error('provider-chain source marker contained no `N. Component — role` rows');
  }
  // Guard against drift in the marker itself: orders must be 1..N, in order,
  // names non-empty, and roles free of the `|` that would break the table.
  providers.forEach((p, i) => {
    if (p.order !== i + 1) {
      throw new Error(
        `provider-chain rows must be numbered 1..N in order; row ${i + 1} is numbered ${p.order}`
      );
    }
    if (!p.name) throw new Error(`provider-chain row ${p.order} is missing a component name`);
    if (!p.role) throw new Error(`provider-chain row ${p.order} is missing a role`);
    if (p.name.includes('|') || p.role.includes('|')) {
      throw new Error(`provider-chain row ${p.order} must not contain a "|" character`);
    }
  });
  return providers;
}

View on GitHub (pinned to a221052e0d)

Solutions

  1. Rewrite each row inside the marker as `N. Component — role` using a real em dash (U+2014) between name and role
  2. Keep the `N.` numbering prefix on every row — unnumbered lines never match
  3. Re-run `node scripts/generate-architecture-docs.mjs` and confirm the rendered table is non-empty

Example fix

// before (rows no longer match the regex):
 * @generated-source:provider-chain
 * Sentry ErrorBoundary - global crash boundary
 * Redux Provider - app-wide store
 * @end-source:provider-chain

// after:
 * @generated-source:provider-chain
 * 1. Sentry ErrorBoundary — global crash boundary
 * 2. Redux Provider — app-wide store
 * @end-source:provider-chain
Defensive patterns

Strategy: validation

Validate before calling

const ROW_RE = /^\s*\*?\s*(\d+)\.\s+(.+?)\s+—\s+(.+?)\s*$/;
const begin = src.indexOf('@generated-source:provider-chain');
const end = src.indexOf('@end-source:provider-chain');
const rows = src.slice(begin, end).split('\n').filter(l => ROW_RE.test(l));
if (rows.length === 0) {
  console.error('Marker block has no `N. Component — role` rows — check the em dash (U+2014) survived editing');
  process.exit(1);
}

Type guard

/** True when a marker line is a parseable `N. Component — role` row (em dash U+2014). */
const ROW_RE = /^\s*\*?\s*(\d+)\.\s+(.+?)\s+—\s+(.+?)\s*$/;
function isProviderChainRow(line) {
  return ROW_RE.test(line);
}

Try / catch

try {
  parseProviderChain(appSource);
} catch (err) {
  if (err.message.includes('no `N. Component — role` rows')) {
    console.error('Rows unreadable — most likely the em dash was replaced with a hyphen/en dash');
  }
  throw err;
}

Prevention

When it happens

Trigger: Rows rewritten with a hyphen `-` or en dash `–` instead of an em dash `—`; rows converted from `1. Foo — bar` to markdown bullets `- Foo — bar`; rows commented out or replaced with free text while the outer markers survived; a two-space or tab variation is fine, but any row without `<digits>. <text> — <text>` is skipped.

Common situations: Auto-correct/IME replacing em dash with a similar glyph; copy-pasting the chain from rendered markdown that normalized the dash; a teammate 'simplifying' the numbered list. Note the repo itself bans em dash U+2014 in i18n strings, so editors with a lint that rewrites dashes hit exactly this.

Related errors


AI-assisted analysis of tinyhumansai/openhuman@a221052e0d (2026-08-16). Data as JSON: /api/errors/12e9683a9391727e. Report an issue: GitHub.