#2538·graphrag

[Bug]: Table provider list() removes interior CSV/Parquet extensions

Author: YusefSyedCreated Sep 2, 2026Updated Sep 2, 2026

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/MemoryStorage reproduction 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:

python
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:

python
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.