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

  1. 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
  2. Log the raw content plus the reported line/column to identify whether it is preamble, fences, or genuine malformation
  3. 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
  4. 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

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

Related errors


AI-assisted analysis of 666ghj/MiroFish@b5b53acc57 (2026-08-14). Data as JSON: /api/errors/fba20371ca398827. Report an issue: GitHub.