zylon-ai/private-gpt · error · MalformedJSON

Failed to parse JSON: {e!s}

Error message

Failed to parse JSON: {e!s}

What it means

Raised inside partial_json_loads when the underlying partial_json_parser.loads call fails with an exception that is neither MalformedJSON nor a JSONDecodeError — the generic branch wraps it in MalformedJSON('Failed to parse JSON: ...') with the original exception chained. Note the deliberate design: partial JSON is tolerated (that is the function's purpose), 'Extra data' payloads are recovered via JSONDecoder.raw_decode, and only truly unexpected failures (wrong input type, non-string, internal errors) hit this wrapper. The {e!s} placeholder is interpolated with the original exception's message.

Source

Thrown at private_gpt/components/llm/utils.py:132

    if add_generation_prompt:
        parts.append("<|im_start|>assistant\n")

    return "\n".join(parts)


# partial_json_parser doesn't support extra data and
# JSONDecorder.raw_decode doesn't support partial JSON
def partial_json_loads(input_str: str, flags: Allow) -> tuple[Any, int]:
    try:
        return (partial_json_parser.loads(input_str, flags), len(input_str))
    except (MalformedJSON, JSONDecodeError, json.JSONDecodeError) as e:
        message = getattr(e, "msg", None)
        if isinstance(message, str) and "Extra data" in message:
            dec = JSONDecoder()
            return dec.raw_decode(input_str)
        raise
    except Exception as e:
        raise MalformedJSON(f"Failed to parse JSON: {e!s}") from e

View on GitHub (pinned to 4a030776a3)

Solutions

  1. Ensure input_str is a str (decode bytes, guard None) before calling partial_json_loads.
  2. Inspect the chained cause (`except MalformedJSON as e: e.__cause__`) to see the real failure.
  3. If parsing complete (not partial) JSON, use json.loads directly.
  4. Pin/upgrade partial_json_parser if the cause indicates a parser bug.

Example fix

# before
obj, idx = partial_json_loads(raw_chunk, flags)  # raw_chunk may be bytes

# after
obj, idx = partial_json_loads(raw_chunk.decode('utf-8') if isinstance(raw_chunk, bytes) else raw_chunk or '', flags)
Defensive patterns

Strategy: try-catch

Validate before calling

if not isinstance(fragment, str):
    fragment = '' if fragment is None else str(fragment)

Type guard

def is_parseable_fragment(s: Any) -> bool:
    return isinstance(s, str)

Try / catch

try:
    obj, consumed = partial_json_loads(fragment, flags)
except MalformedJSON:
    obj, consumed = None, 0  # skip this chunk in the streaming accumulator

Prevention

When it happens

Trigger: Calling partial_json_loads with a non-string input (bytes, None, dict) causing a TypeError inside the parser; parser-internal errors from exotic malformed fragments that are neither decode errors nor 'Extra data'; streaming tool-call/JSON output where the accumulated fragment triggers an unexpected parser exception rather than a normal partial-JSON state.

Common situations: Streaming LLM JSON tool-call accumulation where a chunk yields invalid input types; downstream code passing raw bytes from a socket; version changes in partial_json_parser surfacing new exception types.

Understand the failure class

Related errors


AI-assisted analysis of zylon-ai/private-gpt@4a030776a3 (2026-08-15). Data as JSON: /api/errors/0b08fa4098e887f8. Report an issue: GitHub.