{"record":{"id":"7b850a2db4cb2c2a","repo":"chroma-core/chroma","slug":"unexpected-keys-in-limit-dict-unexpected-keys","errorCode":null,"errorMessage":"Unexpected keys in Limit dict: {unexpected_keys}","messagePattern":"Unexpected keys in Limit dict: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"chromadb/execution/expression/operator.py","lineNumber":590,"sourceCode":"                f\"Limit offset must be an integer, got {type(offset).__name__}\"\n            )\n        if offset < 0:\n            raise ValueError(f\"Limit offset must be non-negative, got {offset}\")\n\n        limit = data.get(\"limit\")\n        if limit is not None:\n            if not isinstance(limit, int):\n                raise TypeError(\n                    f\"Limit limit must be an integer, got {type(limit).__name__}\"\n                )\n            if limit <= 0:\n                raise ValueError(f\"Limit limit must be positive, got {limit}\")\n\n        # Check for unexpected keys\n        allowed_keys = {\"offset\", \"limit\"}\n        unexpected_keys = set(data.keys()) - allowed_keys\n        if unexpected_keys:\n            raise ValueError(f\"Unexpected keys in Limit dict: {unexpected_keys}\")\n\n        return Limit(offset=offset, limit=limit)\n\n\n@dataclass\nclass Projection:\n    document: bool = False\n    embedding: bool = False\n    metadata: bool = False\n    rank: bool = False\n    uri: bool = False\n\n    @property\n    def included(self) -> Include:\n        includes = list()\n        if self.document:\n            includes.append(\"documents\")\n        if self.embedding:","sourceCodeStart":572,"sourceCodeEnd":608,"githubUrl":"https://github.com/chroma-core/chroma/blob/aecdd12c8a891610db8653630b066b32ceb678b5/chromadb/execution/expression/operator.py#L572-L608","documentation":"Limit.from_dict applies closed-schema validation: only 'offset' and 'limit' are accepted, and any extra key raises ValueError listing the offending set. This catches typos and API mix-ups early instead of silently ignoring unknown settings.","triggerScenarios":"Limit.from_dict({'limit': 10, 'page_size': 2}); carrying over the classic kwarg name {'n_results': 10}; typos like {'offsett': 0}; operator-style keys like {'$limit': 10} leaking in from expression dicts.","commonSituations":"Migrating from collection.query(n_results=...) and reusing the old field name; forwarding raw user JSON into Search(limit=...); shared config blocks where unrelated keys ride along.","solutions":["Keep only {'offset', 'limit'} in the dict.","Rename legacy names during migration: n_results -> limit.","Whitelist keys at your boundary, rejecting extras with your own 400 error."],"exampleFix":"# before\nSearch(limit={'n_results': 10, 'offset': 0})   # -> ValueError: Unexpected keys\n\n# after\nSearch(limit={'limit': 10, 'offset': 0})","handlingStrategy":"validation","validationCode":"ALLOWED_LIMIT_KEYS = {'offset', 'limit'}\n\ndef sanitize_limit(data: dict) -> dict:\n    extra = set(data) - ALLOWED_LIMIT_KEYS\n    if extra:\n        raise ValueError(f'unsupported limit keys: {sorted(extra)}')\n    return data","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Map external field names (e.g. n_results) to offset/limit explicitly instead of forwarding raw payloads.","Use a typed schema for paging params at the edge.","Watch for '$'-prefixed keys leaking in from where/rank expressions."],"tags":["validation","valueerror","pagination","schema","chromadb"],"backgroundTag":"unknown-field-rejected","analyzedSha":"aecdd12c8a891610db8653630b066b32ceb678b5","analyzedAt":"2026-08-16T21:53:27.228Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}