HKUDS/Vibe-Trading · critical · ValueError

hypotheses storage must contain a JSON list

Error message

hypotheses storage must contain a JSON list

What it means

The hypotheses storage file parses as valid JSON but its top-level value is not an array (e.g. an object, string, or number). The storage schema requires a JSON list of hypothesis objects, so loading aborts.

Source

Thrown at agent/src/hypotheses/registry.py:376

                score = 1
            else:
                hay_tokens = _tokenize(haystack)
                score = len(query_tokens & hay_tokens)
            if score > 0:
                scored.append((score, hyp))
        scored.sort(key=lambda item: (item[0], item[1].updated_at), reverse=True)
        return [hyp for _, hyp in scored[: max(1, min(int(limit), 100))]]

    def list(self) -> list[Hypothesis]:
        """Load all hypotheses from storage."""
        if not self.path.exists():
            return []
        try:
            raw = json.loads(self.path.read_text(encoding="utf-8"))
        except json.JSONDecodeError as exc:
            raise ValueError(f"invalid hypotheses storage JSON: {self.path}") from exc
        if not isinstance(raw, list):
            raise ValueError("hypotheses storage must contain a JSON list")
        return [Hypothesis.from_dict(item) for item in raw if isinstance(item, dict)]

    def _save(self, records: list[Hypothesis]) -> None:
        payload = [hyp.to_dict() for hyp in sorted(records, key=lambda h: h.created_at)]
        tmp_path = self.path.with_suffix(self.path.suffix + ".tmp")
        tmp_path.write_text(
            json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=True),
            encoding="utf-8",
        )
        tmp_path.replace(self.path)

    @staticmethod
    def _find_required(records: list[Hypothesis], hypothesis_id: str) -> Hypothesis:
        for hyp in records:
            if hyp.hypothesis_id == hypothesis_id:
                return hyp
        raise KeyError(f"hypothesis not found: {hypothesis_id}")

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Rewrap the content as a top-level JSON array of hypothesis objects
  2. If a wrapper object exists, extract the inner list into the root
  3. Validate the file shape after any manual or external-tool edit

Example fix

# before (file content)
{"hypotheses": [{"hypothesis_id": "h1", ...}]}
# after
[{"hypothesis_id": "h1", ...}]
Defensive patterns

Strategy: type-guard

Validate before calling

raw = json.loads(path.read_text(encoding='utf-8'))
if not isinstance(raw, list):
    raw = raw.get('hypotheses', []) if isinstance(raw, dict) else []
    path.write_text(json.dumps(raw), encoding='utf-8')

Type guard

def is_storage_list(raw) -> bool:
    return isinstance(raw, list) and all(isinstance(i, dict) for i in raw)

Try / catch

try:
    records = registry.list()
except ValueError as exc:
    if 'must contain a JSON list' in str(exc):
        rewrite_storage_as_list()  # migration/repair
    else:
        raise

Prevention

When it happens

Trigger: The file contains {"hypotheses": [...]} instead of [...], or a single hypothesis object at the root; then list() and every mutating method fail.

Common situations: Someone reshaped the file by hand, or an export tool wrote an object wrapper; schema drift after a version change of the registry format.

Related errors


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