[Bug]: JsonCache.get() crashes with unhandled AttributeError on non-dict or invalid envelope JSON payloads

Đang mở Phù hợp với người mới
#2,614 2 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 5 ngày

Chưa có ai nhận issue này.

  • #1 của @wangkangmao — đã đóng, không merge

Đánh giá

Độ khó
2/5
Thời gian dự kiến
1-3 giờ
Mức phù hợp với người mới
82/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
python
Lĩnh vực
backend, databases

Hướng nghiên cứu

Đọc packages/graphrag-cache/graphrag_cache/json_cache.py và so sánh JsonCache.get() với cách hoạt động của SQLiteCache được mô tả trong issue. Kiểm tra các bài kiểm tra cache và bổ sung phạm vi kiểm thử cho các giá trị JSON hợp lệ không phải là từ điển và các từ điển thiếu result. Công việc hoàn tất khi các mục không hợp lệ bị xóa và get() trả về None mà không phát sinh ngoại lệ.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

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: main branch (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.
Ngôn ngữ chính
Python
Star
36.6k
Fork
3.9k
Merge trung bình
4 ngày 11 giờ
Pull request đã merge (30 ngày)
1

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của microsoft/graphrag

Tất cả issue của microsoft/graphrag

Issue tương tự

Thêm issue về Python

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.