{"record":{"id":"30ca5e392bafe5ee","repo":"666ghj/MiroFish","slug":"zep-batch-creation-is-unconfirmed-and-no-matching","errorCode":null,"errorMessage":"Zep batch creation is unconfirmed and no matching operation was found","messagePattern":"Zep batch creation is unconfirmed and no matching operation was found","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"backend/app/services/graph_builder.py","lineNumber":448,"sourceCode":"            # Journal the deterministic operation before the server-generated\n            # batch ID POST. This leaves enough identity for later diagnosis\n            # even if both the response and immediate list reconciliation fail.\n            batch_created_callback(None, operation_id)\n\n        try:\n            batch = self.client.batch.create(\n                metadata={\n                    \"mirofish_operation_id\": operation_id,\n                    \"graph_id\": graph_id,\n                    \"chunk_count\": total_chunks,\n                }\n            )\n        except Exception as error:\n            if not is_retryable_zep_error(error):\n                raise\n            batch = self._find_batch_by_operation_id(graph_id, operation_id)\n            if batch is None:\n                raise RuntimeError(\n                    \"Zep batch creation is unconfirmed and no matching operation was found\"\n                ) from error\n        batch_id = getattr(batch, \"batch_id\", None)\n        if not batch_id:\n            raise RuntimeError(\"Zep Batch API returned no batch_id\")\n        if batch_created_callback:\n            batch_created_callback(batch_id, operation_id)\n\n        episode_uuids: List[str] = []\n        for i in range(0, total_chunks, batch_size):\n            batch_chunks = chunks[i:i + batch_size]\n            batch_num = i // batch_size + 1\n            total_batches = (total_chunks + batch_size - 1) // batch_size\n            \n            if progress_callback:\n                progress = (i + len(batch_chunks)) / total_chunks\n                progress_callback(\n                    t('progress.sendingBatch', current=batch_num, total=total_batches, chunks=len(batch_chunks)),","sourceCodeStart":430,"sourceCodeEnd":466,"githubUrl":"https://github.com/666ghj/MiroFish/blob/b5b53acc57189a4a42e44a23e149dc655c98fe82/backend/app/services/graph_builder.py#L430-L466","documentation":"RuntimeError in submit_document_batch: client.batch.create raised a retryable error (per is_retryable_zep_error), so the code tried to reconcile by listing batches for the deterministic operation_id — and found none. That means the create likely never reached the server (or is not yet visible), so there is no batch identity to continue with and the operation cannot be confirmed; raising prevents silently re-POSTing and risking duplicates.","triggerScenarios":"Network failure/timeout before the create request reached Zep; the create was rejected server-side with a retryable-looking status (e.g. 429/503) after no side effects; list-batch eventual consistency delaying the new batch's appearance past the reconcile attempts; Zep Cloud outage.","commonSituations":"Flaky upstream connectivity during long document builds; Zep Cloud incident returning 5xx; rate limiting (429) on batch creation; retries exhausted because _find_batch_by_operation_id's sleep-backoff is short relative to Zep's listing lag.","solutions":["Simply retry the whole build submission once connectivity recovers — no batch exists, so a fresh create is safe (the operation_id journal plus match-check protects against duplicates).","Check Zep Cloud status and the API key's quota/rate limits if 429/503 appears in the chained error.","If it recurs, increase the reconcile attempts/backoff in _find_batch_by_operation_id to ride out listing lag.","Verify no proxy between backend and Zep is eating POST bodies or timing out the create.","Inspect the chained cause (raise ... from error) to distinguish 'never sent' from 'sent but unlisted'."],"exampleFix":"# before\nbatch = self._find_batch_by_operation_id(graph_id, operation_id)\nif batch is None:\n    raise RuntimeError(\"Zep batch creation is unconfirmed and no matching operation was found\") from error\n\n# after - retry create once when reconcile proves nothing was created\nbatch = self._find_batch_by_operation_id(graph_id, operation_id)\nif batch is None:\n    if create_attempted_once:\n        raise RuntimeError(\n            \"Zep batch creation is unconfirmed and no matching operation was found\"\n        ) from error\n    batch = self.client.batch.create(metadata={\"mirofish_operation_id\": operation_id, \"graph_id\": graph_id})\n    create_attempted_once = True","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"for attempt in range(3):\n    try:\n        submission = builder.submit_document_batch(graph_id, chunks, batch_size=bs)\n        break\n    except RuntimeError as e:\n        if 'unconfirmed and no matching operation' in str(e) and attempt < 2:\n            time.sleep(2 ** attempt)\n            continue  # safe: reconcile proved no batch was created\n        raise","preventionTips":["Rely on the deterministic operation_id journal — retrying after a proven no-create is safe; blind re-POSTs are not.","Monitor Zep Cloud status and API-key rate limits when create starts failing with retryable codes.","Give _find_batch_by_operation_id enough attempts/backoff to ride out listing lag.","Always inspect the chained cause to distinguish never-sent from unlisted."],"tags":["backend","python","zep","network","batch","unconfirmed-operation"],"backgroundTag":null,"analyzedSha":"b5b53acc57189a4a42e44a23e149dc655c98fe82","analyzedAt":"2026-08-14T22:29:33.146Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}