{"record":{"id":"be8a33b97a6abc84","repo":"666ghj/MiroFish","slug":"zep-batch-submission-batch-id-ended-as-status","errorCode":null,"errorMessage":"Zep batch {submission.batch_id} ended as {status}; failed_items={len(failed_items)}; first_error={first_error}","messagePattern":"Zep batch (.+?) ended as (.+?); failed_items=(.+?); first_error=(.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"backend/app/services/graph_builder.py","lineNumber":680,"sourceCode":"                        total=submission.item_count,\n                        pending=max(submission.item_count - completed, 0),\n                        elapsed=int(time.time() - start_time),\n                    ),\n                    min(max(percent, 0.0), 1.0),\n                )\n\n            if status in terminal_states:\n                break\n            time.sleep(3)\n\n        items = self._list_batch_items(submission.batch_id)\n        if status != \"succeeded\":\n            failed_items = [\n                item for item in items\n                if getattr(item, \"status\", None) not in {\"succeeded\", \"skipped\"}\n            ]\n            first_error = getattr(failed_items[0], \"error\", None) if failed_items else None\n            raise RuntimeError(\n                f\"Zep batch {submission.batch_id} ended as {status}; \"\n                f\"failed_items={len(failed_items)}; first_error={first_error}\"\n            )\n        if len(items) != submission.item_count:\n            raise RuntimeError(\n                f\"Zep batch {submission.batch_id} contains {len(items)} items, \"\n                f\"expected {submission.item_count}\"\n            )\n\n        ordered_items = sorted(\n            items,\n            key=lambda item: getattr(item, \"sequence_index\", 0) or 0,\n        )\n        episode_uuids: List[str] = []\n        for item in ordered_items:\n            item_status = getattr(item, \"status\", None)\n            episode_uuid = getattr(item, \"episode_uuid\", None)\n            source_uuid = getattr(item, \"source_uuid\", None)","sourceCodeStart":662,"sourceCodeEnd":698,"githubUrl":"https://github.com/666ghj/MiroFish/blob/b5b53acc57189a4a42e44a23e149dc655c98fe82/backend/app/services/graph_builder.py#L662-L698","documentation":"Raised by GraphBuilder._wait_for_batch after the batch reached a terminal state other than 'succeeded' (partial, failed, invalid, or canceled). The code lists all batch items, counts those whose item status is not 'succeeded'/'skipped', extracts the first item's error field, and raises RuntimeError with that diagnostic. This is Zep reporting real per-item failures; the message tells you how many failed and the first server-side error string.","triggerScenarios":"Batch ends in 'partial'/'failed' because some items failed server-side processing (malformed content, graph quota, internal Zep errors); 'invalid' for rejected batch payloads; 'canceled' if the batch was canceled in the Zep console or via API while the client was polling.","commonSituations":"Quota exhaustion or plan limits on Zep Cloud mid-batch; content that trips Zep's processing (unusual encodings, degenerate text); manually canceling a batch from the Zep dashboard during ingestion; transient Zep incidents that mark items failed.","solutions":["Read first_error in the message — it is the server's reason for the first failed item and usually names the real cause (quota, content, auth).","If the cause was transient (5xx-style), resubmit only the failed items as a new batch.","Check Zep console for the batch_id to see per-item statuses and whether it was canceled/invalid.","If 'invalid', re-validate the payload shape before resubmitting; if quota, upgrade or wait for reset before retrying."],"exampleFix":"try:\n    uuids = builder._wait_for_batch(submission, progress_callback=cb)\nexcept RuntimeError as e:\n    # message carries status, failed count, and first server error\n    logger.error('batch failed: %s', e)\n    raise","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    uuids = builder._wait_for_batch(submission)\nexcept RuntimeError as e:\n    msg = str(e)\n    if 'ended as canceled' in msg:\n        handle_cancel(submission)\n    elif 'ended as partial' in msg:\n        resubmit_failed_items(submission)  # parse failed count from msg / item statuses\n    else:\n        raise","preventionTips":["Don't cancel batches from the Zep console while an ingestion job is polling.","Monitor Zep quota before large submissions.","Design resubmission of failed items as a routine path, not an exception."],"tags":["zep","batch-api","partial-failure","ingestion"],"backgroundTag":null,"analyzedSha":"b5b53acc57189a4a42e44a23e149dc655c98fe82","analyzedAt":"2026-08-14T22:29:33.146Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}