{"record":{"id":"a15f8c3ce39addf9","repo":"chroma-core/chroma","slug":"transactional-filter-reads-require-a-positive-limi-a15f8c","errorCode":null,"errorMessage":"transactional filter reads require a positive limit","messagePattern":"transactional filter reads require a positive limit","errorType":"validation","errorClass":"InvalidArgumentError","httpStatus":null,"severity":"error","filePath":"chromadb/api/conditional_http.py","lineNumber":246,"sourceCode":"            raise ValueError(\"conditional transaction cannot span collections\")\n        return scope\n\n    def _require_scope(self) -> ConditionalHttpScope:\n        if self._scope is None:\n            raise ValueError(\"conditional transaction has no collection scope\")\n        return self._scope\n\n    def _validate_get_request(self, request_payload: ConditionalHttpGetPayload) -> None:\n        ids = request_payload.get(\"ids\")\n        if ids is not None:\n            for id in ids:\n                if id in self._buffered_write_ids:\n                    raise _invalid_read_after_write(id)\n            return\n\n        limit = request_payload.get(\"limit\")\n        if not isinstance(limit, int) or limit <= 0:\n            raise InvalidArgumentError(\n                \"transactional filter reads require a positive limit\"\n            )\n\n    def _validate_read_token(\n        self, expected_read_token: Optional[int], actual_read_token: Optional[int]\n    ) -> None:\n        if actual_read_token is None:\n            raise InternalError(\n                \"transactional get response did not include an OCC read token\"\n            )\n        if actual_read_token > _MAX_I64:\n            raise InternalError(\n                f\"transactional read token offset {actual_read_token} exceeds i64 range\"\n            )\n        if expected_read_token is not None and expected_read_token != actual_read_token:\n            raise InternalError(\n                \"transactional read token changed from log upper bound offset \"\n                f\"{expected_read_token} to {actual_read_token}\"","sourceCodeStart":228,"sourceCodeEnd":264,"githubUrl":"https://github.com/chroma-core/chroma/blob/aecdd12c8a891610db8653630b066b32ceb678b5/chromadb/api/conditional_http.py#L228-L264","documentation":"Raised by _validate_get_request (chromadb/api/conditional_http.py:246) when a read inside a conditional HTTP transaction supplies neither ids nor a positive integer limit. Transactional (OCC) reads must have an enumerable read set: id-based reads are bounded by construction, so filter-based reads (where/where_document) must be explicitly bounded with limit so the server can tie the read token to a finite set of records. A missing, zero, negative, or non-int limit (e.g. None, the default for a plain get) is rejected.","triggerScenarios":"Inside a transaction, calling collection.get(where=..., where_document=..., limit=None) or limit=0/negative/string; any transactional get whose payload has ids=None and a limit that is not a positive int. Plain collection.get() with no arguments inside a transaction hits this because limit defaults to None.","commonSituations":"Copying non-transactional code that used unbounded get() into a transactional block; paginating with a limit computed from arithmetic that can yield 0; passing limit as a string from config or env vars.","solutions":["Pass an explicit positive integer limit on every filter-based get inside the transaction","Or switch the read to id-based form (ids=[...]) which needs no limit","If you truly need all matching records, page through with limit + offset while staying inside the transaction"],"exampleFix":"# before\nwith client.transaction() as txn:\n    rows = collection.get(where={\"kind\": \"doc\"})  # limit=None -> error\n# after\nwith client.transaction() as txn:\n    rows = collection.get(where={\"kind\": \"doc\"}, limit=100)","handlingStrategy":"validation","validationCode":"def validate_txn_get(payload: dict) -> None:\n    if payload.get(\"ids\") is None:\n        limit = payload.get(\"limit\")\n        assert isinstance(limit, int) and not isinstance(limit, bool) and limit > 0, \\\n            \"transactional filter reads need ids or a positive int limit\"","typeGuard":null,"tryCatchPattern":"from chromadb.errors import InvalidArgumentError\n\ntry:\n    rows = collection.get(where=f, limit=limit)\nexcept InvalidArgumentError as e:\n    if \"positive limit\" in str(e):\n        rows = collection.get(where=f, limit=100)  # bounded retry\n    else:\n        raise","preventionTips":["Default every transactional filter read to a positive limit (e.g. 100)","Prefer id-based gets inside transactions when you know the ids","Wrap limit arithmetic so it can never produce 0 or None"],"tags":["chromadb","transaction","get","limit","occ"],"backgroundTag":"transaction-validation-failed","analyzedSha":"aecdd12c8a891610db8653630b066b32ceb678b5","analyzedAt":"2026-08-16T21:53:27.228Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}