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

  1. Fill in the missing component identifier in the flagged row (the message names the row number)
  2. Keep names as real exported symbol names (e.g. `CoreStateProvider`) so the doc stays greppable
  3. 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

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


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