microsoft / microsoft/graphrag
[Bug]: Table provider list() removes interior CSV/Parquet extensions
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 36k
- Forks
- 3.8k
- Avg merge
- 20h 40m
- Merged PRs (30d)
- 4
Description
Do you need to file an issue?
- I have searched the existing open and closed issues and pull requests; I found no report or implementation of this behavior.
- Not applicable: this direct
graphrag-storage/MemoryStoragereproduction does not invoke a model provider or model call. - I believe this is a reproducible bug, not a usage question.
Describe the bug
CSVTableProvider.list() and ParquetTableProvider.list() do not preserve table names that contain the provider extension anywhere before the final generated extension.
For example, writing the CSV table name run.csv.2026 creates the expected storage key run.csv.2026.csv, but list() returns run.2026. The returned name cannot be used with has() or read_dataframe() to address the table that was written.
This happens because both implementations use global str.replace() calls when removing the generated file extension:
file.replace(".csv", "")
file.replace(".parquet", "")
The same public round-trip invariant is affected in both providers.
Steps to reproduce
On current main at f40e9a26ce62ba0b3fef8837d24aafdcc6e6c704:
import asyncio
import pandas as pd
from graphrag_storage import StorageConfig, StorageType, create_storage
from graphrag_storage.tables.csv_table_provider import CSVTableProvider
async def main() -> None:
storage = create_storage(StorageConfig(type=StorageType.Memory))
provider = CSVTableProvider(storage=storage)
original = "run.csv.2026"
await provider.write_dataframe(original, pd.DataFrame({"value": [1]}))
listed = provider.list()
print(listed)
print(await provider.has(original))
print(await provider.has(listed[0]))
asyncio.run(main())
Actual output:
['run.2026']
True
False
An equivalent Parquet storage key such as run.parquet.2026.parquet is returned as run.2026 for the same reason.
Expected behavior
list() should remove only the final provider-generated extension and return the original logical table name. In the example above, it should return run.csv.2026, and has(provider.list()[0]) should be True.
I can submit a focused patch that preserves interior extension text in both providers and adds a regression to each provider's unit tests if that scope is welcome.
GraphRAG config used
Not applicable. This reproduces directly against graphrag-storage with MemoryStorage; no model or GraphRAG indexing configuration is involved.
Logs and screenshots
No additional logs or screenshots are needed; the output above is deterministic.
Additional information
- GraphRAG Version: current
main(f40e9a26ce62ba0b3fef8837d24aafdcc6e6c704) - Operating system: macOS 26.6.2 (arm64)
- Python version: 3.13.14
- Existing focused CSV provider suite:
7 passed - Existing focused Parquet provider suite with locked
pyarrow==25.0.1:6 passed - AI assistance disclosure: This report was prepared with AI assistance. The CSV and Parquet reproductions, outputs, source locations, and issue/PR overlap above were checked on the stated revision.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with CSVTableProvider.list() and ParquetTableProvider.list(), then run the focused provider test suites described in the issue using MemoryStorage. Add regression coverage for names containing interior .csv or .parquet text; done means list() returns the original logical name and has(provider.list()[0]) remains true.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- databases
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 86/100