{"record":{"id":"96840a6d946ab8df","repo":"666ghj/MiroFish","slug":"details","errorCode":null,"errorMessage":"部分图谱更新器未完整停止: {details}","messagePattern":"部分图谱更新器未完整停止: (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"backend/app/services/zep_graph_memory_updater.py","lineNumber":782,"sourceCode":"            except Exception as error:\n                # Keep a failed updater registered so the caller can retry and\n                # lifecycle/report guards still see the incomplete ingestion.\n                logger.error(\n                    \"停止更新器失败: simulation_id=%s, error=%s\",\n                    simulation_id,\n                    error,\n                )\n                errors.append((simulation_id, error))\n\n        with cls._lock:\n            cls._stop_all_done = not cls._updaters\n\n        if errors:\n            details = \"; \".join(\n                f\"{simulation_id}: {error}\"\n                for simulation_id, error in errors\n            )\n            raise RuntimeError(f\"部分图谱更新器未完整停止: {details}\")\n        logger.info(\"已停止所有图谱记忆更新器\")\n    \n    @classmethod\n    def get_all_stats(cls) -> Dict[str, Dict[str, Any]]:\n        \"\"\"获取所有更新器的统计信息\"\"\"\n        return {\n            sim_id: updater.get_stats() \n            for sim_id, updater in cls._updaters.items()\n        }\n","sourceCodeStart":764,"sourceCodeEnd":792,"githubUrl":"https://github.com/666ghj/MiroFish/blob/b5b53acc57189a4a42e44a23e149dc655c98fe82/backend/app/services/zep_graph_memory_updater.py#L764-L792","documentation":"Raised by the class-level stop_all(): it attempts to stop every registered updater, collects (simulation_id, error) pairs for failures, and if any stop failed, raises RuntimeError with a joined per-simulation detail string. It is an aggregate error — some updaters may have stopped successfully while others did not; _stop_all_done is set only when the registry is empty.","triggerScenarios":"Application shutdown while one or more updaters raise during stop(): common causes are the drain/ingestion TimeoutErrors (errors 61-63), Zep API failures during final flush, or worker threads refusing to join. Each failed stop is recorded and re-raised together.","commonSituations":"Process exit during active simulations, Zep Cloud unreachable at shutdown, or large buffered backlogs that cannot drain within the shutdown deadline.","solutions":["Parse the per-simulation details in the message to identify exactly which updaters failed and why (the inner error text is included)","Retry stop_all (or stop the specific simulations) with a longer deadline — successful updaters are already gone, only failures remain","If Zep was unreachable, restore connectivity first, then retry","For shutdown code paths, decide explicitly whether to abandon unflushed activities (force stop without drain) rather than failing process exit"],"exampleFix":"# before\ntry:\n    ZepGraphMemoryUpdater.stop_all()\nexcept RuntimeError as e:\n    os._exit(1)\n\n# after\ntry:\n    ZepGraphMemoryUpdater.stop_all()\nexcept RuntimeError as e:\n    logger.error(\"shutdown incomplete: %s\", e)\n    ZepGraphMemoryUpdater.stop_all()  # retried; only failed updaters remain","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    ZepGraphMemoryUpdater.stop_all()\nexcept RuntimeError as e:\n    logger.error(\"partial stop failure: %s\", e)\n    # already-stopped updaters are gone; retry only the failures\n    ZepGraphMemoryUpdater.stop_all()","preventionTips":["Stop updaters individually with generous deadlines before process shutdown","Parse the per-simulation details from the message to target retries","Decide an explicit policy for abandoning unflushed activities at shutdown"],"tags":["zep","shutdown","aggregate-error","lifecycle"],"backgroundTag":null,"analyzedSha":"b5b53acc57189a4a42e44a23e149dc655c98fe82","analyzedAt":"2026-08-14T22:29:33.146Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}