AI & Agent Dev Bug Sandbox logo
AI & Agent Dev Bug Sandbox
Back to Radar

Core: `InMemoryRecordManager.List_keys(Limit=0)` Returns All Keys Instead Of An Empty List

The `limit` parameter is treated as falsy when `0`, causing all keys to be returned instead of an empty list for both `list_keys` and `alist_keys`.

mediumConfidence 95%Langchain-CoreAffected V<=1.6.3

Origin Analysis

Python's truthiness evaluation: `if limit:` treats `0` as `False` and skips the slicing, effectively treating `limit=0` as no limit. The method should only use `None` to mean no cap.
```python from langchain_core.indexing import InMemoryRecordManager manager = InMemoryRecordManager(namespace="demo") manager.create_schema() manager.update(["a", "b", "c"]) print(manager.list_keys(limit=0)) # Actual: ['a', 'b', 'c']; Expected: [] assert manager.list_keys(limit=0) == [] # fails ```

Fixing Code Block

def list_keys(self, before: Optional[float] = None, limit: Optional[int] = None) -> List[str]: result = list(self.records.keys()) if before is not None: result = [k for k in result if self.records[k] < before] if limit is not None: return result[:limit] return result async def alist_keys(self, before: Optional[float] = None, limit: Optional[int] = None) -> List[str]: return self.list_keys(before, limit)
Replace the truthiness check `if limit:` with an explicit `if limit is not None:`. This treats `limit=0` as a valid zero limit and returns an empty slice, while `limit=None` still returns all results.

Edge Case Audit

This changes behavior only for `limit=0`; any external code that previously used `limit=0` to mean "unlimited" will break. That was not the documented contract, but rollback would mean reverting to `if limit:` or instructing users to pass `None` for unlimited. Concurrency: `InMemoryRecordManager` is not thread-safe and this fix does not alter that. For multi-threaded use, external locking remains required.

Ecosystem Topology