{"record":{"id":"5d7127d5244cb861","repo":"666ghj/MiroFish","slug":"zep-graph-add-returned-no-episode-uuid","errorCode":null,"errorMessage":"Zep graph.add returned no episode UUID","messagePattern":"Zep graph\\.add returned no episode UUID","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"backend/app/services/zep_graph_memory_updater.py","lineNumber":524,"sourceCode":"                        \"last_round\": max(a.round_num for a in payload_activities),\n                        \"agent_ids\": \",\".join(\n                            str(value)\n                            for value in sorted({a.agent_id for a in payload_activities})\n                        ),\n                        \"action_types\": \",\".join(\n                            value\n                            for value in sorted({a.action_type for a in payload_activities})\n                            if value\n                        ) or \"unknown\",\n                    },\n                )\n\n                episode_uuid = (\n                    getattr(episode, \"uuid_\", None)\n                    or getattr(episode, \"uuid\", None)\n                )\n                if not episode_uuid:\n                    raise RuntimeError(\"Zep graph.add returned no episode UUID\")\n                self._pending_episode_uuids.append(str(episode_uuid))\n                self._total_sent += 1\n                self._total_items_sent += len(payload_activities)\n                display_name = self._get_platform_display_name(platform)\n                logger.info(f\"成功批量发送 {len(payload_activities)} 条{display_name}活动到图谱 {self.graph_id}\")\n                logger.debug(f\"批量内容预览: {combined_text[:200]}...\")\n\n            except Exception as e:\n                # graph.add has no idempotency key. Replaying an ambiguous\n                # response can duplicate extracted facts, so fail closed and\n                # surface the incomplete batch to SimulationRunner.\n                logger.error(f\"批量发送到Zep失败，未自动重放非幂等写入: {e}\")\n                self._failed_count += 1\n                self._failed_batches.append({\n                    \"platform\": platform,\n                    \"activities\": payload_activities,\n                    \"error\": str(e),\n                })","sourceCodeStart":506,"sourceCodeEnd":542,"githubUrl":"https://github.com/666ghj/MiroFish/blob/b5b53acc57189a4a42e44a23e149dc655c98fe82/backend/app/services/zep_graph_memory_updater.py#L506-L542","documentation":"Raised in ZepGraphMemoryUpdater after a successful-looking call to Zep Cloud's graph.add: the returned episode object exposes neither a 'uuid_' nor a 'uuid' attribute, so the updater cannot track the episode for later ingestion polling. Because graph.add is not idempotent, the updater deliberately fails closed instead of retrying — replaying an ambiguous response could duplicate extracted facts in the knowledge graph.","triggerScenarios":"Calling client.graph.add(...) (batch of platform activities) where the server response body omits the episode UUID field, or a Zep Cloud SDK version whose episode model names the field differently than uuid_/uuid. Also possible if the API silently changed its response schema or returned a degraded/error object that still parsed.","commonSituations":"Zep Cloud API/SDK version drift (response schema change), a misconfigured graph_id causing a partial response, or a proxy/gateway stripping response fields. Typically surfaces mid-simulation while flushing buffered activities.","solutions":["Log the raw episode object (repr/dir) at the failure site to see which fields Zep actually returned","Pin the zep-cloud SDK to a tested version and check its changelog for episode response changes","If the field was renamed, extend the getattr chain in zep_graph_memory_updater.py:519-522 to include the new attribute name","Do NOT add automatic retry around graph.add — it is non-idempotent; a retry can duplicate extracted facts","Verify the graph_id exists and the API key has write permission on that graph"],"exampleFix":"# before\nepisode_uuid = (\n    getattr(episode, \"uuid_\", None)\n    or getattr(episode, \"uuid\", None)\n)\nif not episode_uuid:\n    raise RuntimeError(\"Zep graph.add returned no episode UUID\")\n\n# after\nepisode_uuid = (\n    getattr(episode, \"uuid_\", None)\n    or getattr(episode, \"uuid\", None)\n)\nif not episode_uuid:\n    logger.error(\"Unexpected graph.add response: %r\", episode)\n    raise RuntimeError(\"Zep graph.add returned no episode UUID\")","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    episode = client.graph.add(...)\nexcept RuntimeError:\n    # fail closed: do NOT replay graph.add (non-idempotent); surface to runner\n    raise","preventionTips":["Pin the zep-cloud SDK version and test after upgrades","Log the raw episode object whenever the UUID is missing to catch schema drift early","Never wrap graph.add in automatic retries without an idempotency key"],"tags":["zep","graph-memory","sdk-compat","integration"],"backgroundTag":null,"analyzedSha":"b5b53acc57189a4a42e44a23e149dc655c98fe82","analyzedAt":"2026-08-14T22:29:33.146Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}