{"record":{"id":"397c90ad030faa9b","repo":"666ghj/MiroFish","slug":"zep-graph-updater-is-not-running","errorCode":null,"errorMessage":"Zep graph updater is not running","messagePattern":"Zep graph updater is not running","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"warning","filePath":"backend/app/services/zep_graph_memory_updater.py","lineNumber":378,"sourceCode":"        - LIKE_POST/DISLIKE_POST（点赞/踩帖子）\n        - REPOST（转发）\n        - FOLLOW（关注）\n        - MUTE（屏蔽）\n        - LIKE_COMMENT/DISLIKE_COMMENT（点赞/踩评论）\n        \n        action_args中会包含完整的上下文信息（如帖子原文、用户名等）。\n        \n        Args:\n            activity: Agent活动记录\n        \"\"\"\n        # 跳过DO_NOTHING类型的活动\n        if activity.action_type == \"DO_NOTHING\":\n            self._skipped_count += 1\n            return\n\n        with self._acceptance_lock:\n            if not self._running:\n                raise RuntimeError(\"Zep graph updater is not running\")\n            self._activity_queue.put(activity)\n            self._total_activities += 1\n        logger.debug(f\"添加活动到Zep队列: {activity.agent_name} - {activity.action_type}\")\n    \n    def add_activity_from_dict(self, data: Dict[str, Any], platform: str):\n        \"\"\"\n        从字典数据添加活动\n        \n        Args:\n            data: 从actions.jsonl解析的字典数据\n            platform: 平台名称 (twitter/reddit)\n        \"\"\"\n        # 跳过事件类型的条目\n        if \"event_type\" in data:\n            return\n        if data.get(\"success\") is False:\n            self._skipped_count += 1\n            return","sourceCodeStart":360,"sourceCodeEnd":396,"githubUrl":"https://github.com/666ghj/MiroFish/blob/b5b53acc57189a4a42e44a23e149dc655c98fe82/backend/app/services/zep_graph_memory_updater.py#L360-L396","documentation":"add_activity() guards the accepting state: under _acceptance_lock it checks self._running, and once stop() has flipped _running to False it rejects further activities with RuntimeError('Zep graph updater is not running'). DO_NOTHING activities are skipped before this check (just counted), so the error only fires for real actions enqueued after shutdown began.","triggerScenarios":"A producer thread (action-file tailer or platform runner) calling add_activity after stop() started — the classic race the acceptance lock exists to close; or a caller reusing an updater instance after stop() completed.","commonSituations":"Simulation actions still being parsed/fed while the runner is finalizing; retry logic that keeps pumping activities after a stop request; tests that stop the updater then feed more events.","solutions":["Make producers stop before calling stop(): signal the action source, join it, then stop the updater.","Catch this RuntimeError in the producer as an expected shutdown signal and exit the feed loop cleanly.","Never reuse an updater after stop(); construct a new one (new graph_id) for a subsequent run."],"exampleFix":"# before\ndef on_action(activity):\n    updater.add_activity(activity)  # RuntimeError once stop() begins\n\n# after\ndef on_action(activity):\n    try:\n        updater.add_activity(activity)\n    except RuntimeError:\n        logger.info(\"updater closed; dropping activity feed\")\n        stop_event.set()  # unwind producers before stop()","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    updater.add_activity(activity)\nexcept RuntimeError as e:\n    if \"not running\" in str(e):\n        stop_feed.set()  # expected during shutdown; unwind producer\n        return\n    raise","preventionTips":["Stop producers (action tailers) and join them before calling updater.stop().","Treat this RuntimeError as a shutdown signal in feed loops, not a bug.","Never reuse an updater after stop(); build a fresh instance per run."],"tags":["zep","updater","race-condition","shutdown","producer-consumer"],"backgroundTag":null,"analyzedSha":"b5b53acc57189a4a42e44a23e149dc655c98fe82","analyzedAt":"2026-08-14T22:29:33.146Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}