{"record":{"id":"3b3daf033ac03498","repo":"666ghj/MiroFish","slug":"zep-updater-drain-deadline-elapsed","errorCode":null,"errorMessage":"Zep updater drain deadline elapsed","messagePattern":"Zep updater drain deadline elapsed","errorType":"exception","errorClass":"TimeoutError","httpStatus":null,"severity":"error","filePath":"backend/app/services/zep_graph_memory_updater.py","lineNumber":594,"sourceCode":"                buffer = list(self._platform_buffers.get(platform, []))\n            if not buffer:\n                continue\n            display_name = self._get_platform_display_name(platform)\n            logger.info(f\"发送{display_name}平台剩余的 {len(buffer)} 条活动\")\n            if deadline is not None and time.time() >= deadline:\n                raise TimeoutError(\n                    \"Zep updater drain deadline elapsed before flushing all activities\"\n                )\n            try:\n                processed_count = self._send_batch_activities(\n                    buffer,\n                    platform,\n                    deadline=deadline,\n                )\n            except _DrainDeadlineExceeded as error:\n                with self._buffer_lock:\n                    del self._platform_buffers[platform][:error.processed_count]\n                raise TimeoutError(str(error)) from error\n            else:\n                with self._buffer_lock:\n                    del self._platform_buffers[platform][:processed_count]\n\n    def _wait_for_pending_episodes(self, *, deadline: float | None = None) -> None:\n        pending = set(self._pending_episode_uuids)\n        if not pending:\n            return\n\n        if deadline is None:\n            deadline = time.time() + ZEP_INGESTION_WAIT_TIMEOUT_SECONDS\n        while pending:\n            if time.time() >= deadline:\n                raise TimeoutError(\n                    f\"Zep simulation ingestion timed out with {len(pending)} \"\n                    \"episode(s) pending\"\n                )\n            for episode_uuid in list(pending):","sourceCodeStart":576,"sourceCodeEnd":612,"githubUrl":"https://github.com/666ghj/MiroFish/blob/b5b53acc57189a4a42e44a23e149dc655c98fe82/backend/app/services/zep_graph_memory_updater.py#L576-L612","documentation":"Raised when _send_batch_activities internally aborts with _DrainDeadlineExceeded: the batch send ran out of deadline partway through. The handler first trims the platform buffer by error.processed_count (activities already accepted are removed) and then re-raises TimeoutError, so a resume re-sends only the unsent tail — no duplicates, no data loss.","triggerScenarios":"A platform's buffered batch is large enough that sending it crosses the deadline mid-batch; _send_batch_activities detects the deadline between chunked writes and raises _DrainDeadlineExceeded carrying the count of already-processed activities.","commonSituations":"End-of-simulation flush with a deadline sized for smaller batches, slow Zep ingestion throughput, or per-chunk retries (call_zep_read_with_retry style backoff) eating the remaining budget.","solutions":["Catch TimeoutError at the SimulationRunner level and re-invoke drain with a new deadline — processed items were already trimmed from the buffer","Reduce batch size so each _send_batch_activities call fits comfortably inside the remaining budget","Scale the deadline to backlog size instead of using a constant","Investigate Zep throughput (rate limits, network) if deadlines are regularly exceeded"],"exampleFix":"# before\ntry:\n    updater.drain(deadline=deadline)\nexcept TimeoutError:\n    abort_simulation()\n\n# after\ntry:\n    updater.drain(deadline=deadline)\nexcept TimeoutError:\n    deadline = time.time() + EXTENDED_BUDGET\n    updater.drain(deadline=deadline)  # buffer was trimmed; only unsent tail remains","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    processed = updater._send_batch_activities(buffer, platform, deadline=deadline)\nexcept _DrainDeadlineExceeded as e:\n    # buffer already trimmed to processed_count; only unsent tail remains\n    retry_later(remaining=buffer[e.processed_count:])","preventionTips":["Keep batches small enough to fit inside the remaining deadline budget","Check remaining deadline before starting each batch send","Treat processed_count as authoritative to avoid duplicate sends on resume"],"tags":["zep","timeout","partial-write","resume"],"backgroundTag":null,"analyzedSha":"b5b53acc57189a4a42e44a23e149dc655c98fe82","analyzedAt":"2026-08-14T22:29:33.146Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}