tinyhumansai/openhuman · error · Error
provider-chain row ${p.order} is missing a component name
Error message
provider-chain row ${p.order} is missing a component name What it means
Defensive guard in parseProviderChain(): a row matched the regex but its captured component name trims to the empty string. Because the regex requires at least one character before the em dash, this only fires when that character(s) is pure whitespace — i.e. the row has a role but the component-name slot was left blank.
Source
Thrown at scripts/generate-architecture-docs.mjs:76
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;
}
/**
* Render the markdown body that lives between the BEGIN/END markers.
* Returned as an array of lines (no trailing join) so splicing stays
* deterministic and line-oriented.
*
* @param {{ order: number, name: string, role: string }[]} providers
* @returns {string[]}
*/
export function renderProviderChainBody(providers) {
return [View on GitHub (pinned to a221052e0d)
Solutions
- Fill in the missing component identifier in the flagged row (the message names the row number)
- Keep names as real exported symbol names (e.g. `CoreStateProvider`) so the doc stays greppable
- Re-run the generator to confirm the row parses
Example fix
// before: * 3. — provides auth snapshot via fetchCoreAppSnapshot() // after: * 3. CoreStateProvider — provides auth snapshot via fetchCoreAppSnapshot()
Defensive patterns
Strategy: validation
Validate before calling
for (const m of rows.map(l => ROW_RE.exec(l))) {
if (!m[2].trim()) {
console.error(`Row ${m[1]} has an empty component name`);
process.exit(1);
}
} Prevention
- Use real exported component names (CoreStateProvider, SocketProvider, …) in the marker rows so empty names are obvious in review
- Diff the marker block whenever App.tsx provider code changes; the name column should mirror the code
When it happens
Trigger: A marker row like `* 3. — runs the boot gate` where the author deleted the component name but kept numbering and the em dash. Any single stray space before the dash satisfies `(.+?)` while trimming empty.
Common situations: Hand-editing a row to swap in a new component and accidentally deleting the name; placeholder rows (`3. TBD — role`) don't hit this (TBD is non-empty) but fully blanked names do.
Related errors
- provider-chain rows must be numbered 1..N in order; row ${i
- provider-chain row ${p.order} must not contain a "|" charact
- generated-block END marker must come after the BEGIN marker
- provider-chain source marker not found in App.tsx; expected
- provider-chain source marker contained no `N. Component — ro
AI-assisted analysis of tinyhumansai/openhuman@a221052e0d (2026-08-16).
Data as JSON: /api/errors/b09e852df11610bb.
Report an issue: GitHub.