JuliusBrussee/caveman · error · CatalogError
: catalog must be a non-empty list
Error message
{path}: catalog must be a non-empty list What it means
CatalogError raised by load_yaml() when the catalog file parses as valid YAML but the top-level document is not a list, or is an empty list. A catalog must be a non-empty list of row objects; anything else (a dict, a string, null, or []) is rejected immediately.
Solutions
- Make the top level a YAML sequence of mappings — one '- ...' entry per provider row, no wrapper key
- If entries were removed, restore at least one row; empty catalogs are invalid by design
- Confirm you pointed the tool at the actual catalog file, not notes or a README
- If a producer emits a single object, wrap it: put the object as the sole list element
Example fix
# before (catalog.yaml)
providers:
- name: acme
// after (top-level list)
- name: acme
models:
- id: acme-large Defensive patterns
Strategy: validation
Validate before calling
import yaml
def catalog_shape_ok(path: str) -> bool:
doc = yaml.safe_load(open(path, encoding="utf-8"))
return isinstance(doc, list) and len(doc) > 0 and all(isinstance(r, dict) for r in doc)
# check before calling load_yaml Type guard
def is_nonempty_row_list(doc) -> bool:
return isinstance(doc, list) and len(doc) > 0 and all(isinstance(r, dict) for r in doc) Try / catch
try:
rows = load_yaml(Path(path))
except CatalogError as e:
if "non-empty list" in str(e):
log.error("catalog root must be a YAML list of objects")
raise Prevention
- Author catalogs as a top-level YAML sequence with no wrapper key
- Keep at least one row in every shipped catalog; empty files are invalid
- Add a schema check step in CI that asserts the root is a non-empty list of objects
- Confirm the CLI/script path argument points at the catalog, not notes or docs
When it happens
Trigger: Running validate_catalog against a YAML file whose root is a mapping (e.g. starts with 'providers:') instead of a top-level sequence, or a file containing only comments/null, or an empty file.
Common situations: Authoring the catalog wrapped in an extra key; a file where all entries were deleted leaving an empty list; a README or notes file accidentally passed as the catalog path; a tool exporting a single object instead of a list of objects.
Related errors
- catalog
- : currency must be USD
- : must be a safe non-empty identifier
- : invalid YAML
- Aider config has duplicate openai-api-base keys; refusing…
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/06bf6f52af9c408b.
Report an issue: GitHub.
Appendix: source
Thrown at shared/provider-catalog/validate_catalog.py:95
# marker intact attests a price check nobody performed. Deleting the marker is
# the reviewer's statement that they confirmed the number against the provider's
# own pricing page; until then, the catalog is invalid.
# MUST stay a prefix of REVIEW_MARKER_PREFIX in that script (the test suite
# asserts it), or a renamed marker would sail past this rule.
REVIEW_MARKER = "# proposed-by:"
class CatalogError(ValueError):
pass
def load_yaml(path: Path) -> list[dict[str, Any]]:
try:
document = yaml.safe_load(path.read_text(encoding="utf-8"))
except (OSError, yaml.YAMLError) as error:
raise CatalogError(f"{path}: invalid YAML: {error}") from error
if not isinstance(document, list) or not document:
raise CatalogError(f"{path}: catalog must be a non-empty list")
if not all(isinstance(row, dict) for row in document):
raise CatalogError(f"{path}: every catalog row must be an object")
return document
def parsed_time(value: Any, label: str) -> datetime:
if isinstance(value, datetime):
parsed = value
elif isinstance(value, str):
try:
parsed = datetime.fromisoformat(value.replace("Z", "+00:00"))
except ValueError as error:
raise CatalogError(f"{label}: verified_at must be RFC3339") from error
else:
raise CatalogError(f"{label}: verified_at must be an RFC3339 timestamp")
if parsed.tzinfo is None:
raise CatalogError(f"{label}: verified_at must include a timezone")
return parsed.astimezone(timezone.utc)View on GitHub (pinned to 3ee70a1026)