{"record":{"id":"e88a17cf068ad1b3","repo":"bytedance/deer-flow","slug":"cursor-must-be-0-and-limit-must-be-1","errorCode":null,"errorMessage":"cursor must be >= 0 and limit must be >= 1","messagePattern":"cursor must be >= 0 and limit must be >= 1","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"backend/packages/harness/deerflow/agents/memory/backends/deermem/deermem/core/storage.py","lineNumber":1220,"sourceCode":"        with self._scope_lock(key), _process_file_lock(path.parent / \".memory.lock\", float(getattr(self._config, \"file_lock_timeout_seconds\", 10))):\n            self._recover_if_needed(path)\n            if legacy_path.exists():\n                _, _, notifications = self._migrate_locked(path, agent_name, user_id=user_id, include_global=False)\n            fact, _ = self._read_fact(path, fact_id, user_id=user_id, agent_name=agent_name)\n        self._dispatch_retrieval_notifications(notifications, user_id=user_id, agent_name=agent_name)\n        return copy.deepcopy(fact)\n\n    def list_facts(\n        self,\n        *,\n        user_id: str | None = None,\n        agent_name: str | None = None,\n        filters: dict[str, Any] | None = None,\n        cursor: int = 0,\n        limit: int = 100,\n    ) -> list[dict[str, Any]]:\n        if cursor < 0 or limit < 1:\n            raise ValueError(\"cursor must be >= 0 and limit must be >= 1\")\n        facts = self.load(agent_name, user_id=user_id).get(\"facts\", [])\n        filters = filters or {}\n        matched = [fact for fact in facts if all(key in fact and fact.get(key) == value for key, value in filters.items())]\n        return copy.deepcopy(matched[cursor : cursor + limit])\n\n    def apply_changes(\n        self,\n        change_set: dict[str, Any],\n        *,\n        user_id: str | None = None,\n        agent_name: str | None = None,\n        expected_manifest_revision: int | None = None,\n        allow_manifest_rebase: bool = False,\n    ) -> dict[str, Any]:\n        \"\"\"Commit an incremental change set and return only the applied delta.\n\n        ``complete`` is deliberately false: callers that require the historical\n        full document must explicitly call ``load``.  This prevents a fresh","sourceCodeStart":1202,"sourceCodeEnd":1238,"githubUrl":"https://github.com/bytedance/deer-flow/blob/1dd6ba1acb03700589994b0366c5d1c7d05e2eff/backend/packages/harness/deerflow/agents/memory/backends/deermem/deermem/core/storage.py#L1202-L1238","documentation":"list_facts() paginates an in-memory filtered list with Python slicing (matched[cursor:cursor+limit]); the guard rejects cursor < 0 or limit < 1 before doing anything. These are pure argument-contract errors, not data errors - the underlying facts are not inspected.","triggerScenarios":"list_facts(cursor=-1), list_facts(limit=0), or values derived from request query params / arithmetic that can go negative (e.g. cursor = offset - page_size with offset < page_size).","commonSituations":"Exposing pagination params straight from an HTTP API without clamping; computing cursor from a 'previous page' calculation that underflows; passing limit=0 meaning 'no limit' (not supported here).","solutions":["Clamp inputs before calling: cursor = max(0, cursor); limit = max(1, min(limit, max_page_size)).","Validate at the API boundary (FastAPI Query(ge=0) / Query(ge=1)) so bad values never reach storage.","If you meant 'fetch everything', pass a large finite limit rather than 0."],"exampleFix":"# before\nfacts = storage.list_facts(cursor=offset - page_size, limit=0, agent_name=a)\n\n# after\ncursor = max(0, offset - page_size)\nfacts = storage.list_facts(cursor=cursor, limit=max(1, page_size), agent_name=a)","handlingStrategy":"validation","validationCode":"cursor = max(0, int(cursor or 0))\nlimit = max(1, min(int(limit or 100), 500))\nfacts = storage.list_facts(cursor=cursor, limit=limit, agent_name=agent_name)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Clamp pagination params at the API boundary (FastAPI Query(ge=0)/Query(ge=1)).","Never use limit=0 to mean unlimited; pick a finite cap.","Compute 'previous page' cursors with max(0, ...) to avoid underflow."],"tags":["memory","pagination","validation","deermem"],"backgroundTag":null,"analyzedSha":"1dd6ba1acb03700589994b0366c5d1c7d05e2eff","analyzedAt":"2026-08-14T21:20:34.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}