{"record":{"id":"0b08fa4098e887f8","repo":"zylon-ai/private-gpt","slug":"failed-to-parse-json-e-s","errorCode":null,"errorMessage":"Failed to parse JSON: {e!s}","messagePattern":"Failed to parse JSON: (.+?)","errorType":"exception","errorClass":"MalformedJSON","httpStatus":null,"severity":"error","filePath":"private_gpt/components/llm/utils.py","lineNumber":132,"sourceCode":"    if add_generation_prompt:\n        parts.append(\"<|im_start|>assistant\\n\")\n\n    return \"\\n\".join(parts)\n\n\n# partial_json_parser doesn't support extra data and\n# JSONDecorder.raw_decode doesn't support partial JSON\ndef partial_json_loads(input_str: str, flags: Allow) -> tuple[Any, int]:\n    try:\n        return (partial_json_parser.loads(input_str, flags), len(input_str))\n    except (MalformedJSON, JSONDecodeError, json.JSONDecodeError) as e:\n        message = getattr(e, \"msg\", None)\n        if isinstance(message, str) and \"Extra data\" in message:\n            dec = JSONDecoder()\n            return dec.raw_decode(input_str)\n        raise\n    except Exception as e:\n        raise MalformedJSON(f\"Failed to parse JSON: {e!s}\") from e\n","sourceCodeStart":114,"sourceCodeEnd":133,"githubUrl":"https://github.com/zylon-ai/private-gpt/blob/4a030776a31a901ad80b1bf4d7faa2c1a367efbb/private_gpt/components/llm/utils.py#L114-L133","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure input_str is a str (decode bytes, guard None) before calling partial_json_loads.","Inspect the chained cause (`except MalformedJSON as e: e.__cause__`) to see the real failure.","If parsing complete (not partial) JSON, use json.loads directly.","Pin/upgrade partial_json_parser if the cause indicates a parser bug."],"exampleFix":"# before\nobj, idx = partial_json_loads(raw_chunk, flags)  # raw_chunk may be bytes\n\n# after\nobj, idx = partial_json_loads(raw_chunk.decode('utf-8') if isinstance(raw_chunk, bytes) else raw_chunk or '', flags)","handlingStrategy":"try-catch","validationCode":"if not isinstance(fragment, str):\n    fragment = '' if fragment is None else str(fragment)","typeGuard":"def is_parseable_fragment(s: Any) -> bool:\n    return isinstance(s, str)","tryCatchPattern":"try:\n    obj, consumed = partial_json_loads(fragment, flags)\nexcept MalformedJSON:\n    obj, consumed = None, 0  # skip this chunk in the streaming accumulator","preventionTips":["Type-check stream chunks (str) before feeding the accumulator; decode bytes at the source.","Log e.__cause__ when MalformedJSON escapes to identify the real parser failure."],"tags":["json","streaming","parsing","llm-output"],"backgroundTag":null,"analyzedSha":"4a030776a31a901ad80b1bf4d7faa2c1a367efbb","analyzedAt":"2026-08-15T03:51:26.951Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}