{"record":{"id":"64bcdda002f29343","repo":"HKUDS/Vibe-Trading","slug":"audit-rows-must-be-objects","errorCode":null,"errorMessage":"audit rows must be objects","messagePattern":"audit rows must be objects","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/tools/goal_tool.py","lineNumber":47,"sourceCode":"        return [value] if value.strip() else []\n    if isinstance(value, list):\n        return [str(item).strip() for item in value if str(item).strip()]\n    return []\n\n\ndef _coerce_audit_rows(value: Any) -> list[AuditRow]:\n    \"\"\"Coerce model/API-style audit rows into dataclasses.\"\"\"\n    if value in (None, \"\"):\n        return []\n    if isinstance(value, str):\n        value = json.loads(value)\n    if not isinstance(value, list):\n        raise ValueError(\"audit must be a list\")\n\n    rows: list[AuditRow] = []\n    for item in value:\n        if not isinstance(item, dict):\n            raise ValueError(\"audit rows must be objects\")\n        criterion_id = str(item.get(\"criterion_id\") or \"\").strip()\n        result = str(item.get(\"result\") or \"\").strip()\n        if not criterion_id or not result:\n            raise ValueError(\"audit rows require criterion_id and result\")\n        rows.append(\n            AuditRow(\n                criterion_id=criterion_id,\n                result=result,\n                evidence_ids=_coerce_string_list(item.get(\"evidence_ids\")),\n                notes=str(item.get(\"notes\") or \"\"),\n            )\n        )\n    return rows\n\n\ndef _sha256_file(path: Path) -> str:\n    \"\"\"Return the sha256 digest for a local artifact.\"\"\"\n    digest = hashlib.sha256()","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/tools/goal_tool.py#L29-L65","documentation":"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.","triggerScenarios":"audit=[\"pass\", \"fail\"] or audit=[[{...}]] where elements are strings or nested arrays instead of objects.","commonSituations":"LLM emitting CSV-like rows, double-encoded JSON strings inside the list, or mixed content from template fills.","solutions":["Ensure every element is a dict with at least criterion_id and result keys","json.loads twice if elements arrived as JSON strings: audit=[json.loads(x) for x in audit]","Validate the payload shape before calling execute"],"exampleFix":"# before\nexecute(audit=[\"c1:pass\"])\n# after\nexecute(audit=[{\"criterion_id\": \"c1\", \"result\": \"pass\"}])","handlingStrategy":"validation","validationCode":"rows = [json.loads(x) if isinstance(x, str) else x for x in audit]\nassert all(isinstance(r, dict) for r in rows), \"audit rows must be dicts\"","typeGuard":"def all_rows_are_objects(audit: list) -> bool:\n    return all(isinstance(r, dict) for r in audit)","tryCatchPattern":"try:\n    execute(audit=audit)\nexcept ValueError as e:\n    if \"must be objects\" in str(e):\n        audit = [json.loads(r) for r in audit if isinstance(r, str)]\n        execute(audit=audit)\n    raise","preventionTips":["Double-decode stringified rows","Validate with a schema (pydantic) before calling the tool"],"tags":["python","json","validation","llm-io"],"backgroundTag":"schema-validation-failed","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}