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
- Rewrite each row inside the marker as `N. Component — role` using a real em dash (U+2014) between name and role
- Keep the `N.` numbering prefix on every row — unnumbered lines never match
- 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
- Type the em dash (U+2014) deliberately when editing the marker block; verify with a regex search for `—` in App.tsx
- Paste chain edits as plain text with auto-correct disabled — smart-dash substitution is the usual culprit
- Run `node scripts/generate-architecture-docs.mjs` locally right after editing the block; it parses the rows before touching any doc
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
- provider-chain source marker not found in App.tsx; expected
- provider-chain rows must be numbered 1..N in order; row ${i
- provider-chain row ${p.order} is missing a component name
- provider-chain row ${p.order} must not contain a "|" charact
- generated-block markers not found in ${FRONTEND_DOC}; expect
AI-assisted analysis of tinyhumansai/openhuman@a221052e0d (2026-08-16).
Data as JSON: /api/errors/12e9683a9391727e.
Report an issue: GitHub.