virattt/ai-hedge-fund · error · LLMParseError
no JSON object found in response: {text[:200]!r}
Error message
no JSON object found in response: {text[:200]!r} What it means
Raised by extract_json (hedge_fund/llm/client.py:222) as LLMParseError (a ValueError) when the LLM response contains no parsable JSON object. The function tries three strategies — a ```json fenced block, the whole string, then the first balanced {...} sequence via brace counting — and only raises after all fail, including when a balanced block exists but json.loads rejects it.
Source
Thrown at hedge_fund/llm/client.py:222
return json.loads(text.strip())
except json.JSONDecodeError:
pass
start = text.find("{")
if start != -1:
depth = 0
for i, ch in enumerate(text[start:], start):
if ch == "{":
depth += 1
elif ch == "}":
depth -= 1
if depth == 0:
try:
return json.loads(text[start : i + 1])
except json.JSONDecodeError:
break
raise LLMParseError(f"no JSON object found in response: {text[:200]!r}")
View on GitHub (pinned to eff8a7320f)
Solutions
- Retry the LLM call — transient non-JSON output often resolves on re-ask; the calling agent layer should catch LLMParseError and retry once or twice.
- Strengthen the prompt: demand 'Respond with ONLY a JSON object {"signal": ..., "confidence": ..., "reasoning": ...}' and, if the transport supports it, use a JSON/response-format mode.
- Raise max_tokens in make_llm so the JSON is never truncated.
- If the model returns arrays or nested junk, pre-normalize (take the first dict from a list) before extract_json.
Example fix
# before
sig = agent.generate_signal(ticker, snapshot) # model replied in prose -> LLMParseError
# after
from hedge_fund.llm.client import LLMParseError
for attempt in range(3):
try:
sig = agent.generate_signal(ticker, snapshot)
break
except LLMParseError:
if attempt == 2:
raise
# re-ask; optionally append 'Answer with JSON only.' to the prompt Defensive patterns
Strategy: retry
Validate before calling
import json
def response_likely_has_json(text: str) -> bool:
"""Cheap pre-check mirroring extract_json's strategy order."""
if "```" in text:
return True
if text.strip().startswith("{") and text.strip().endswith("}"):
try:
json.loads(text)
return True
except json.JSONDecodeError:
pass
return "{" in text and "}" in text Type guard
from hedge_fund.llm.client import LLMParseError
def is_llm_parse_failure(e: BaseException) -> bool:
return isinstance(e, LLMParseError) Try / catch
from hedge_fund.llm.client import LLMParseError
for attempt in range(3):
try:
sig = agent.generate_signal(ticker, snapshot)
return sig
except LLMParseError:
if attempt == 2:
raise # persistent: surface it, don't fabricate a neutral signal
# optional: append 'Respond with ONLY a JSON object.' to the re-ask Prevention
- Pin the output format in the prompt and include one worked JSON example.
- Catch LLMParseError and re-ask once or twice — non-JSON output is usually transient.
- Budget max_tokens generously so the JSON is never truncated mid-object.
- Never map a parse failure to a 'neutral' signal silently — that is the lookahead/feedback bug this library's fail-loud policy exists to avoid.
When it happens
Trigger: An LLM agent signal (LLMAgent._parse calls extract_json on the model's reply) where the model: answers in prose with no braces at all; emits single-quoted or trailing-comma JSON (json.loads fails inside the balanced-brace attempt, breaking out of the scan); wraps the object in a fence labeled something other than json; returns an empty string; prefixes a long chatty preamble that makes the first {...} close at the wrong place (depth returns to 0 early on inline braces).
Common situations: Weaker/cheaper models ignoring the JSON-format instruction; prompt changed to allow prose; max_tokens set so low the JSON is truncated mid-object; model returning a JSON array [...] instead of an object {...}.
Related errors
- invalid signal {data.get('signal')!r}
- confidence out of range: {confidence}
- {method} {path} rate limited (429) after {len(self._RETRY_DE
- No v2 client for {provider} (model {model}). Supported: {',
- {env_var} not found. Set it in your .env to use {provider} m
AI-assisted analysis of virattt/ai-hedge-fund@eff8a7320f (2026-08-15).
Data as JSON: /api/errors/62dd721861b9ebc8.
Report an issue: GitHub.