[Bug]: JsonCache.get() crashes with unhandled AttributeError on non-dict or invalid envelope JSON payloads
Los mantenedores suelen responder en 5 días
Nadie ha tomado este issue todavía.
- #1 de @wangkangmao — cerrado sin fusionar
Evaluación
- Dificultad
- 2/5
- Tiempo estimado
- 1-3 horas
- Aptitud para principiantes
- 82/100
Línea de trabajo
Lee packages/graphrag-cache/graphrag_cache/json_cache.py y compara JsonCache.get() con el comportamiento de SQLiteCache descrito en el issue. Revisa las pruebas de caché y añade cobertura para valores JSON válidos que no sean diccionarios y para diccionarios a los que les falte result. La tarea se considera terminada cuando se eliminan las entradas no válidas y get() devuelve None sin lanzar una excepción.
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.
- I believe this is a legitimate bug, not just a question. If this is a question, please use the Discussions area.
Describe the bug
In packages/graphrag-cache/graphrag_cache/json_cache.py, JsonCache.get() decodes stored cache data using json.loads(data) and assumes the deserialized object is always a dictionary:
async def get(self, key: str) -> Any:
"""Get method definition."""
if await self.has(key):
try:
data = await self._storage.get(key)
data = json.loads(data)
except UnicodeDecodeError:
await self._storage.delete(key)
return None
except json.decoder.JSONDecodeError:
await self._storage.delete(key)
return None
else:
return data.get("result")
If the storage contains a valid JSON payload that is not a dictionary (e.g. a JSON array [1, 2, 3], primitive string, number, or boolean from an external tool, corrupted state, or older schema format), json.loads() succeeds without raising JSONDecodeError. Then execution jumps to data.get("result"), immediately raising:
AttributeError: 'list' object has no attribute 'get'
Furthermore, unlike SQLiteCache (which explicitly handles invalid envelopes and purges corrupted entries via if not isinstance(data, dict) or "result" not in data:), JsonCache neither catches this exception nor deletes the offending key. As a result, every subsequent call to cache.get(key) repeatedly crashes the pipeline instead of recovering gracefully with None.
Steps to reproduce
Self-contained minimal reproduction (no external APIs or configs needed):
import asyncio
import json
class InMemoryStorage:
def __init__(self):
self._store = {}
async def get(self, key):
return self._store.get(key)
async def set(self, key, value):
self._store[key] = value
async def has(self, key):
return key in self._store
async def delete(self, key):
self._store.pop(key, None)
# Directly reproduces packages/graphrag-cache/graphrag_cache/json_cache.py
class JsonCache:
def __init__(self, storage):
self._storage = storage
async def get(self, key: str):
if await self.has(key):
try:
data = await self._storage.get(key)
data = json.loads(data)
except UnicodeDecodeError:
await self._storage.delete(key)
return None
except json.decoder.JSONDecodeError:
await self._storage.delete(key)
return None
else:
return data.get("result")
return None
async def has(self, key: str) -> bool:
return await self._storage.has(key)
async def main():
storage = InMemoryStorage()
cache = JsonCache(storage)
# Store a valid JSON array or primitive payload under a cache key
await storage.set("bad_envelope", "[1, 2, 3]")
# Crashes with unhandled AttributeError: 'list' object has no attribute 'get'
result = await cache.get("bad_envelope")
print("Result:", result)
asyncio.run(main())
Expected Behavior
Consistent with SQLiteCache, JsonCache.get() should verify that isinstance(data, dict) and "result" in data. If invalid, it should purge the corrupted key from storage and safely return None.
Proposed fix:
async def get(self, key: str) -> Any | None:
"""Get method definition."""
if await self.has(key):
try:
data = await self._storage.get(key)
data = json.loads(data)
except (UnicodeDecodeError, json.decoder.JSONDecodeError):
await self._storage.delete(key)
return None
else:
if not isinstance(data, dict) or "result" not in data:
await self._storage.delete(key)
return None
return data["result"]
return None
GraphRAG Config Used
N/A (Reproducible directly in graphrag_cache.json_cache.JsonCache)
Logs and screenshots
Traceback (most recent call last):
File "repro.py", line 47, in <module>
asyncio.run(main())
File ".../asyncio/runners.py", line 190, in run
return runner.run(main)
File ".../asyncio/runners.py", line 118, in run
return self._loop.run_until_complete(task)
File ".../asyncio/base_events.py", line 654, in run_until_complete
return future.result()
File "repro.py", line 43, in main
result = await cache.get("bad_envelope")
File "repro.py", line 32, in get
return data.get("result")
AttributeError: 'list' object has no attribute 'get'
Additional context
- GraphRAG Version:
mainbranch (post-monorepo split / PR #2591) - Python Version: Python 3.10 / 3.11 / 3.12
- Operating System: Linux / macOS / Windows
- Note: We have prepared a fix with regression tests and can submit a PR if this looks good to maintainers.
- 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
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 74/100
microsoft/graphrag#2626 · 1 comentario ·
Los mantenedores suelen responder en 5 días
-
[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]: 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