HKUDS/Vibe-Trading · error · ValueError

manifest file {manifest_path} must be a JSON object with a '

Error message

manifest file {manifest_path} must be a JSON object with a 'runs' array or a bare JSON array of run specs

What it means

After parsing, load_manifest expects either a bare JSON array of run specs or a JSON object containing a 'runs' key whose value is a list. This variant fires when the top level is a dict but parsed['runs'] is not a list — e.g. it's missing, an object, a string, or null. The runs list is what gets returned for validation.

Source

Thrown at agent/src/tools/strategy_discovery_tool.py:483

        raw = manifest_path.read_text(encoding="utf-8")
    except OSError as exc:
        raise ValueError(
            f"manifest file {manifest_path} is missing or unreadable "
            f"({exc.strerror or 'I/O error'})"
        ) from exc
    try:
        parsed = json.loads(raw)
    except json.JSONDecodeError as exc:
        raise ValueError(
            f"manifest file {manifest_path} is not valid JSON: {exc.msg} "
            f"at line {exc.lineno} column {exc.colno}"
        ) from exc
    if isinstance(parsed, list):
        return parsed
    if isinstance(parsed, dict):
        runs = parsed.get("runs")
        if not isinstance(runs, list):
            raise ValueError(
                f"manifest file {manifest_path} must be a JSON object with a "
                "'runs' array or a bare JSON array of run specs"
            )
        return runs
    raise ValueError(
        f"manifest file {manifest_path} must be a JSON object with a 'runs' "
        "array or a bare JSON array of run specs"
    )


def validate_refresh_entries(
    entries: list,
) -> tuple[list[dict], list[dict]]:
    """Split raw manifest entries into rebuild-ready specs and skip records.

    Per-entry validation (plan §4.6/D7): the entry must be a mapping with a
    non-empty ``strategy_id`` (capped at ``_MAX_STRING_PARAM_CHARS``) and a
    non-empty ``run_dir`` that resolves (expanduser + resolve) inside the

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Rename the key to exactly "runs" and make its value a JSON array
  2. If runs are keyed by id (object), flatten to a list: {"runs": list(your_dict.values())} or [your_dict[k] for k in sorted(your_dict)]
  3. Check the tool's run-spec schema/docs for the expected manifest shape

Example fix

# before
{ "runs": { "r1": {"path": "..."}, "r2": {"path": "..."} } }
# after
{ "runs": [ {"path": "..."}, {"path": "..."} ] }
Defensive patterns

Strategy: validation

Validate before calling

import json
data = json.loads(Path(manifest_path).read_text(encoding="utf-8"))
runs = data if isinstance(data, list) else data.get("runs") if isinstance(data, dict) else None
if not isinstance(runs, list):
    raise ValueError("manifest must be a list or {'runs': [...]}")
core(manifest_path=manifest_path, ...)

Type guard

def has_valid_runs_shape(data) -> bool:
    return isinstance(data, list) or (isinstance(data, dict) and isinstance(data.get("runs"), list))

Prevention

When it happens

Trigger: Manifest like {"results": [...]} (wrong key), {"runs": {"a": 1}} (object not array), or {"runs": null}; any dict whose 'runs' value fails isinstance(runs, list).

Common situations: Schema drift between manifest writers and readers (key renamed to 'experiments'); wrapping run specs in a nested object for grouping; exporting from a tool that emits runs as a dict keyed by run id.

Related errors


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