JuliusBrussee/caveman · error

${label}:${lineNo}: expected a top-level "- provider: ..." r

Error message

${label}:${lineNo}: expected a top-level "- provider: ..." row

What it means

Thrown by the hand-rolled catalog parser in generate-agent-catalog.mjs when it reads a non-empty, non-comment line at indent 0 that does not start with "- ". The catalog YAML is constrained to a list of top-level rows, each beginning "- provider: <value>"; a bare key at column 0 violates that shape.

Source

Thrown at scripts/generate-agent-catalog.mjs:61

const REGION_AGNOSTIC = "global";
const REQUIRED_CURRENCY = "USD";

/** Parses the flat `- key: value` catalog sequence. Throws on any other shape. */
export function parseCatalogYaml(text, label = CATALOG_LABEL) {
  const rows = [];
  let row = null;
  let blockKey = null;
  const lines = text.split("\n");
  for (let index = 0; index < lines.length; index++) {
    const line = lines[index].replace(/\s+$/, "");
    const lineNo = index + 1;
    if (line === "" || /^ *#/.test(line)) continue;
    const shape = /^( *)(- )?(.*)$/.exec(line);
    const indent = shape[1].length;
    const dash = shape[2] !== undefined;
    const rest = shape[3];
    if (indent === 0) {
      if (!dash) throw new Error(`${label}:${lineNo}: expected a top-level "- provider: ..." row`);
      row = {};
      rows.push(row);
      blockKey = null;
      const entry = splitKeyValue(rest, label, lineNo);
      if (entry.value === undefined) throw new Error(`${label}:${lineNo}: row must start with an inline "provider" value`);
      row[entry.key] = entry.value;
      continue;
    }
    if (row === null) throw new Error(`${label}:${lineNo}: indented line before any row`);
    if (indent === 2) {
      if (dash) throw new Error(`${label}:${lineNo}: unexpected sequence item at row level`);
      const entry = splitKeyValue(rest, label, lineNo);
      if (Object.prototype.hasOwnProperty.call(row, entry.key)) {
        throw new Error(`${label}:${lineNo}: duplicate key "${entry.key}"`);
      }
      if (entry.value === undefined) {
        blockKey = entry.key;
        row[entry.key] = undefined;

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Make every top-level entry a sequence row: prefix it with "- " and start with an inline provider value, e.g. "- provider: anthropic".
  2. Remove leftover merge-conflict markers or stray lines at column 0 (the error's label:lineNo pinpoints the exact line).
  3. If you need a document-level key, that shape is unsupported — restructure the data so the file stays a flat list of provider rows.

Example fix

# before
provider: anthropic
  model: claude

# after
- provider: anthropic
  model: claude
Defensive patterns

Strategy: validation

Validate before calling

function topLevelRowsOk(text) {
  return text.split("\n").every((line) => {
    const t = line.trimEnd();
    return t === "" || /^ *#/.test(t) || /^- provider: \S/.test(t) || /^ {2,4} /.test(t);
  });
}

Try / catch

try {
  parseCatalog(text);
} catch (err) {
  if (/expected a top-level/.test(err.message)) {
    // label:lineNo points at the offending line — open it and add the "- " prefix
  } else throw err;
}

Prevention

When it happens

Trigger: Editing the agent catalog source file and adding a top-level mapping key (e.g. "providers:" as a section header) or a plain "provider: x" line without the leading dash, then running the generator script.

Common situations: Converting the file from a list-of-maps to a keyed map shape; merge conflict markers (<<<<<<<) left at indent 0; pasting YAML from another doc that uses a different structure.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/79317d9c6a0aea8d. Report an issue: GitHub.