langflow-ai/langflow · error · SystemExit
error: {source} top-level value must be an object
Error message
error: {source} top-level value must be an object What it means
Printed and raised (exit code 2) by _parse in check_migration_append_only.py when the migration-table JSON parses successfully but its top-level value is not an object — e.g. the file is a JSON array or a bare string/number. The schema requires an object with `entries` (and optional `ambiguous_bare_names`) lists, so the script refuses to compare.
Source
Thrown at scripts/migrate/check_migration_append_only.py:96
# "no baseline" and return None.
return None
return completed.stdout
def _parse(raw: str, *, source: str) -> tuple[list[dict], list[dict]]:
"""Return ``(entries, ambiguous_bare_names)`` from a migration-table JSON.
Both lists default to empty when the field is absent so this script can
compare across baselines that pre-date a given field.
"""
try:
data = json.loads(raw)
except json.JSONDecodeError as exc:
print(f"error: invalid JSON in {source}: {exc}", file=sys.stderr)
raise SystemExit(2) from exc
if not isinstance(data, dict):
print(f"error: {source} top-level value must be an object", file=sys.stderr)
raise SystemExit(2)
entries = data.get("entries", [])
if not isinstance(entries, list):
print(f"error: {source} entries field must be a list", file=sys.stderr)
raise SystemExit(2)
ambig = data.get("ambiguous_bare_names", [])
if not isinstance(ambig, list):
print(f"error: {source} ambiguous_bare_names field must be a list", file=sys.stderr)
raise SystemExit(2)
return entries, ambig
def _compare(baseline: list[dict], current: list[dict]) -> list[str]:
"""Return human-readable violations; empty list means clean."""
violations: list[str] = []
current_by_key: dict[tuple[str, str], dict] = {}
for entry in current:
try:
key = _entry_key(entry)View on GitHub (pinned to 976ec789d2)
Solutions
- Wrap the content in an object with the required keys: {"entries": [...], "ambiguous_bare_names": [...]}
- Confirm `entries` and (if present) `ambiguous_bare_names` are lists, or the next validation will also fail
- If the format change is intentional, update _parse and downstream comparison logic in the same commit
Example fix
# before
[
{"name": "0001_initial", "digest": "abc"}
]
# after
{
"entries": [
{"name": "0001_initial", "digest": "abc"}
],
"ambiguous_bare_names": []
} Defensive patterns
Strategy: type-guard
Validate before calling
import json
data = json.loads(raw)
if not isinstance(data, dict) or not isinstance(data.get("entries", []), list):
raise ValueError("migration table must be an object with an `entries` list") Type guard
def is_migration_table(data: object) -> bool:
return (
isinstance(data, dict)
and isinstance(data.get("entries", []), list)
and isinstance(data.get("ambiguous_bare_names", []), list)
) Try / catch
# the script exits 2; catch at the CI step level
data = json.loads(raw)
if not isinstance(data, dict):
print(f"expected object, got {type(data).__name__}"); sys.exit(2) Prevention
- Keep the migration-table schema fixed: top-level object with entries/ambiguous_bare_names lists
- Generate the file from typed tooling rather than by hand
- If changing the format, update check_migration_append_only.py in the same commit
When it happens
Trigger: Rewriting the migration table as a bare array of entries ([{...},{...}]) or wrapping it in a string; json.loads succeeds, the isinstance(data, dict) check fails, and the script exits 2.
Common situations: Refactoring the table format without updating the script's expectation; generating the file with a tool that emits top-level arrays; partially applied format migrations.
Related errors
- error: invalid JSON in {source}: {exc}
- git is not available; cannot check append-only invariant.
- Invalid flow data
- Invalid JSON format.
- No valid MCP server found in the input.
AI-assisted analysis of langflow-ai/langflow@976ec789d2 (2026-08-14).
Data as JSON: /api/errors/dc93397491c2e988.
Report an issue: GitHub.