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
- Make every top-level entry a sequence row: prefix it with "- " and start with an inline provider value, e.g. "- provider: anthropic".
- Remove leftover merge-conflict markers or stray lines at column 0 (the error's label:lineNo pinpoints the exact line).
- 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
- Validate catalog edits with the generator script in CI so structure errors never reach release.
- Use the exact two-space indentation convention and start every entry with "- provider: <value>".
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
- ${label}:${lineNo}: row must start with an inline "provider"
- ${label}:${lineNo}: indented line before any row
- ${label}:${lineNo}: unexpected sequence item at row level
- ${label}:${lineNo}: duplicate key "${entry.key}"
- ${label}:${lineNo}: nested line without an open block
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/79317d9c6a0aea8d.
Report an issue: GitHub.