666ghj/MiroFish · error · LLMResponseError
LLM returned invalid JSON (line {strict_error.lineno}, colum
Error message
LLM returned invalid JSON (line {strict_error.lineno}, column {strict_error.colno}) What it means
LLMResponseError raised when the response content fails both json.loads and a lenient raw_decode from position 0 — the text is not a JSON object starting at the beginning, and the client explicitly refuses to repair or invent truncated JSON. The original strict error's line/column are reported for diagnosis.
Source
Thrown at backend/app/utils/llm_client.py:269
)
content = _clean_chat_text(extract_chat_completion_text(response))
if not content:
raise LLMResponseError(
"LLM returned empty JSON content",
finish_reason=finish_reason,
)
try:
value = json.loads(content)
except json.JSONDecodeError as strict_error:
# Some compatible providers append a short explanation after an
# otherwise complete JSON object. Accept only an object decoded
# from the beginning; never repair or invent truncated JSON.
try:
value, end = json.JSONDecoder().raw_decode(content)
except json.JSONDecodeError:
raise LLMResponseError(
"LLM returned invalid JSON "
f"(line {strict_error.lineno}, column {strict_error.colno})",
finish_reason=finish_reason,
) from strict_error
trailing = content[end:].strip()
if trailing:
if _contains_additional_json_container(trailing):
raise LLMResponseError(
"LLM returned multiple JSON values",
finish_reason=finish_reason,
)
logger.warning("Ignoring text after a complete LLM JSON object")
if not isinstance(value, dict):
raise LLMResponseError(
"LLM JSON response must be a top-level JSON object",
finish_reason=finish_reason,View on GitHub (pinned to b5b53acc57)
Solutions
- Force a JSON-only response mode: use the provider's response_format={'type': 'json_object'} if supported, or a system prompt that forbids any text outside the JSON
- Log the raw content plus the reported line/column to identify whether it is preamble, fences, or genuine malformation
- If the model persistently adds prose, strip known wrappers (code fences, leading text up to the first '{') before calling the parser — but never repair broken JSON
- Retry: single malformed outputs are often non-deterministic
Example fix
# before
resp = client.chat.completions.create(model=m, messages=msgs)
value = LLMClient._parse_json_response(resp)
# after
resp = client.chat.completions.create(
model=m, messages=msgs,
response_format={"type": "json_object"}, # provider-enforced JSON
)
value = LLMClient._parse_json_response(resp) Defensive patterns
Strategy: retry
Validate before calling
def looks_like_json_object(text: str) -> bool:
t = text.lstrip()
return t.startswith('{') and t.rstrip().endswith('}') Try / catch
try:
value = LLMClient._parse_json_response(resp)
except LLMResponseError as e:
if "invalid JSON" in str(e):
value = LLMClient._parse_json_response(retry_with_stricter_prompt())
else:
raise Prevention
- Enable response_format={'type': 'json_object'} on providers that support it
- Forbid prose/fences in the system prompt
- Strip only known safe wrappers (fences, leading prose) — never repair broken JSON
When it happens
Trigger: Model wraps JSON in markdown fences or prose ('Here is the JSON: {...}') such that raw_decode from offset 0 fails, or the JSON itself is malformed/hand-edited by the model (trailing commas, unquoted keys in strict mode, single quotes).
Common situations: Models that preamble before JSON despite instructions, prompt templates without a strict JSON-only format, or truncation that corrupted structure (though plain truncation usually surfaces as error 73 when finish_reason is 'length').
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- LLM returned multiple JSON values
- Ontology result must be an object
- LLM JSON output was truncated at the token limit
- LLM JSON generation stopped unexpectedly ({finish_reason})
- LLM returned empty JSON content
AI-assisted analysis of 666ghj/MiroFish@b5b53acc57 (2026-08-14).
Data as JSON: /api/errors/fba20371ca398827.
Report an issue: GitHub.