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
- Rewrap the content as a top-level JSON array of hypothesis objects
- If a wrapper object exists, extract the inner list into the root
- 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
- Validate the file shape after any external tool writes it
- Version the storage schema and run migrations on load
- Don't restructure the file format by hand
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
- invalid hypotheses storage JSON: {self.path}
- sources must be a list
- each portfolio source must be an object
- agent config 'channels' must be an object
- agent config 'channels.feishu' must be an object
AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28).
Data as JSON: /api/errors/07da8835efeee7b3.
Report an issue: GitHub.