[Bug]: try_parse_json_object turns backslash escapes into spaces when valid JSON comes in a code fence or after a sentence
Los mantenedores suelen responder en 5 días
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 2/5
- Tiempo estimado
- 1-3 horas
- Aptitud para principiantes
- 74/100
Línea de trabajo
Empieza en packages/graphrag/graphrag/query/llm/text_utils.py en try_parse_json_object, donde el fragmento desde el primer { hasta el último } pasa por la limpieza de barras invertidas con .replace antes de cualquier comprobación de validez. Ejecuta el script de reproducción del issue para confirmar que line1\nline2 devuelve line1 nline2, y comprueba que GlobalSearch._parse_search_response en el search.py de la búsqueda global muestra el mismo daño. Está terminado cuando el JSON válido recortado conserva sus escapes, la entrada inválida sigue pasando por la limpieza antigua y por json_repair, y una prueba de regresión cubre respuestas con cercas de código y con prefijos.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Do you need to file an issue?
- I have searched the existing issues and this bug is not already filed.
- My model is hosted on OpenAI or Azure. If not, please look at the "model providers" issue and don't file a new one here.
- I believe this is a legitimate bug, not just a question. If this is a question, please use the Discussions area.
Describe the bug
try_parse_json_object in packages/graphrag/graphrag/query/llm/text_utils.py first calls json.loads on the whole response. If the model wraps valid JSON in a markdown fence or puts a sentence before it, that call fails. The function then cuts out the text from the first { to the last } and runs a cleanup chain on it. One step of that chain is .replace("\\", " "). It runs before anything checks whether the cut-out text is already valid JSON.
So every escape inside a string value is broken, even though the cut-out text was valid:
\nbecomes a space and the letter n.line1\nline2comes back asline1 nline2.\"becomes a space and a quote.He said \"hi\" todaycomes back asHe said "hi " today.\\becomes two spaces.C:\\data\\x.txtcomes back asC: data x.txt.\t,\rand\u00e9are broken the same way.
The same JSON without the fence parses correctly, because the first json.loads succeeds.
This function parses model output in three query paths:
- the global search map step,
GlobalSearch._parse_search_response(thedescriptiontext of each key point) rate_relevancyin dynamic community selection- DRIFT,
DriftAction.search(theresponsetext andfollow_up_queries)
Map and DRIFT answers are markdown, so they often contain \n. A fenced map response is a real case: #1173 shows gemma2 on ollama returning the map response inside a ```json fence. Also, #2625 shows that response_format_json_object=True does not reach the model on main, and it reports that Qwen and Mistral on vLLM sometimes answer with prose around the JSON. I did not find a trace from OpenAI or Azure, so this mostly affects local and other non-JSON-mode providers.
Back in #801 a maintainer review asked for a json.loads check first, so valid JSON would skip the string cleanup (done in #834). That check only covers the whole response. It does not cover the object cut out of a fenced or prefixed response.
Steps to reproduce
No model is needed. With graphrag installed from main:
import json
from graphrag.query.llm.text_utils import try_parse_json_object
from graphrag.query.structured_search.global_search.search import GlobalSearch
BS = "\\"
raw = "```json\n" + '{"d": "line1' + BS + 'nline2"}' + "\n```"
expected = json.loads(raw[raw.index("{") : raw.rindex("}") + 1])
_, got = try_parse_json_object(raw, verbose=False)
print("expected:", repr(expected))
print("got: ", repr(got))
map_raw = (
"```json\n"
+ '{"points": [{"description": "first line' + BS + 'nsecond line, said '
+ BS + '"ok' + BS + '"", "score": 80}]}'
+ "\n```"
)
print(GlobalSearch._parse_search_response(None, map_raw))
Expected Behavior
When the cut-out object is already valid JSON, it should be returned as parsed. The cleanup and json_repair should only run when it is not valid.
Expected output:
expected: {'d': 'line1\nline2'}
got: {'d': 'line1\nline2'}
[{'answer': 'first line\nsecond line, said "ok"', 'score': 80}]
GraphRAG Config Used
# Not needed. The reproduction calls the parser directly and uses no settings file.
Logs and screenshots
Output on main:
expected: {'d': 'line1\nline2'}
got: {'d': 'line1 nline2'}
[{'answer': 'first line nsecond line, said "ok "', 'score': 80}]
Additional Information
- GraphRAG Version: 3.3.0 (main at 34b2b67)
- Operating System: Windows 11
- Python Version: 3.13
- Related Issues: #1173 (fenced map response from gemma2 on ollama, fixed the regex then). #2625 (JSON mode is not sent to the model). Point 2 of #2625 also shows the backslash cleanup splitting single-quoted follow-up queries at
\'. That input is not valid JSON, so the fix below does not change it. - I have a small fix with a regression test. After the cut-out step it tries
json.loadsand returns the result when it is a non-empty dict. Everything else still goes through the old cleanup path. I will open a PR that references this issue.
Validated with Claude Opus 5.5.
- Lenguaje dominante
- Python
- Estrellas
- 36.6k
- Forks
- 3.9k
- Merge medio
- 4 d 11 h
- PR fusionados (30 d)
- 1
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Tiene una plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de microsoft/graphrag
-
[Bug]: response_format_json_object=True is silently dropped, so JSON mode never reaches the modelAbiertobug triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
microsoft/graphrag#2625 · 1 comentario ·
Los mantenedores suelen responder en 5 días
-
Dificultad 1/5 Menos de una hora Aptitud para principiantes 95/100
Los mantenedores suelen responder en 5 días
-
[Bug]: JsonCache.get() crashes with unhandled AttributeError on non-dict or invalid envelope JSON payloadsQuizá libre de nuevo Un pull request para esta issue se cerró sin fusionarse. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
microsoft/graphrag#2614 · 2 comentarios ·
Los mantenedores suelen responder en 5 días
-
[Bug]: JSONLinesFileReader drops rows containing U+2028, U+2029 or U+0085 (uses str.splitlines)Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 85/100
microsoft/graphrag#2594 · 1 comentario ·
Los mantenedores suelen responder en 5 días
-
[Bug]: TokenChunker never terminates when chunking overlap >= size (no validation of size/overlap)Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
microsoft/graphrag#2593 · 2 comentarios ·
Los mantenedores suelen responder en 5 días
Todos los issues de microsoft/graphrag
Issues similares
-
Input: placeholder drawn in the text colour when the empty Input is focused but not yet editedAbierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
pierreaubert/gpui-toolkit#114 ·
Los mantenedores suelen responder en 1 día
-
intake mcp-intake needs-human-review priority:high type:bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 66/100
Ikalus1988/MisakaNet#4903 · 2 comentarios ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 70/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 64/100
FinanceFlash/unvibecode#819 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
Los mantenedores suelen responder en 1 día