HKUDS/Vibe-Trading · error · ValueError

audit rows must be objects

Error message

audit rows must be objects

What it means

After confirming audit is a list, each element must be a JSON object (dict) mapping criterion_id/result/evidence_ids/notes. Non-dict elements such as strings, numbers, or nested lists raise this. It is row-level schema enforcement for the goal tool's audit trail.

Source

Thrown at agent/src/tools/goal_tool.py:47

        return [value] if value.strip() else []
    if isinstance(value, list):
        return [str(item).strip() for item in value if str(item).strip()]
    return []


def _coerce_audit_rows(value: Any) -> list[AuditRow]:
    """Coerce model/API-style audit rows into dataclasses."""
    if value in (None, ""):
        return []
    if isinstance(value, str):
        value = json.loads(value)
    if not isinstance(value, list):
        raise ValueError("audit must be a list")

    rows: list[AuditRow] = []
    for item in value:
        if not isinstance(item, dict):
            raise ValueError("audit rows must be objects")
        criterion_id = str(item.get("criterion_id") or "").strip()
        result = str(item.get("result") or "").strip()
        if not criterion_id or not result:
            raise ValueError("audit rows require criterion_id and result")
        rows.append(
            AuditRow(
                criterion_id=criterion_id,
                result=result,
                evidence_ids=_coerce_string_list(item.get("evidence_ids")),
                notes=str(item.get("notes") or ""),
            )
        )
    return rows


def _sha256_file(path: Path) -> str:
    """Return the sha256 digest for a local artifact."""
    digest = hashlib.sha256()

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Ensure every element is a dict with at least criterion_id and result keys
  2. json.loads twice if elements arrived as JSON strings: audit=[json.loads(x) for x in audit]
  3. Validate the payload shape before calling execute

Example fix

# before
execute(audit=["c1:pass"])
# after
execute(audit=[{"criterion_id": "c1", "result": "pass"}])
Defensive patterns

Strategy: validation

Validate before calling

rows = [json.loads(x) if isinstance(x, str) else x for x in audit]
assert all(isinstance(r, dict) for r in rows), "audit rows must be dicts"

Type guard

def all_rows_are_objects(audit: list) -> bool:
    return all(isinstance(r, dict) for r in audit)

Try / catch

try:
    execute(audit=audit)
except ValueError as e:
    if "must be objects" in str(e):
        audit = [json.loads(r) for r in audit if isinstance(r, str)]
        execute(audit=audit)
    raise

Prevention

When it happens

Trigger: audit=["pass", "fail"] or audit=[[{...}]] where elements are strings or nested arrays instead of objects.

Common situations: LLM emitting CSV-like rows, double-encoded JSON strings inside the list, or mixed content from template fills.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28). Data as JSON: /api/errors/64bcdda002f29343. Report an issue: GitHub.