666ghj/MiroFish · error · RuntimeError
Zep batch {submission.batch_id} ended as {status}; failed_it
Error message
Zep batch {submission.batch_id} ended as {status}; failed_items={len(failed_items)}; first_error={first_error} What it means
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.
Source
Thrown at backend/app/services/graph_builder.py:680
total=submission.item_count,
pending=max(submission.item_count - completed, 0),
elapsed=int(time.time() - start_time),
),
min(max(percent, 0.0), 1.0),
)
if status in terminal_states:
break
time.sleep(3)
items = self._list_batch_items(submission.batch_id)
if status != "succeeded":
failed_items = [
item for item in items
if getattr(item, "status", None) not in {"succeeded", "skipped"}
]
first_error = getattr(failed_items[0], "error", None) if failed_items else None
raise RuntimeError(
f"Zep batch {submission.batch_id} ended as {status}; "
f"failed_items={len(failed_items)}; first_error={first_error}"
)
if len(items) != submission.item_count:
raise RuntimeError(
f"Zep batch {submission.batch_id} contains {len(items)} items, "
f"expected {submission.item_count}"
)
ordered_items = sorted(
items,
key=lambda item: getattr(item, "sequence_index", 0) or 0,
)
episode_uuids: List[str] = []
for item in ordered_items:
item_status = getattr(item, "status", None)
episode_uuid = getattr(item, "episode_uuid", None)
source_uuid = getattr(item, "source_uuid", None)View on GitHub (pinned to b5b53acc57)
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.
Example fix
try:
uuids = builder._wait_for_batch(submission, progress_callback=cb)
except RuntimeError as e:
# message carries status, failed count, and first server error
logger.error('batch failed: %s', e)
raise Defensive patterns
Strategy: try-catch
Try / catch
try:
uuids = builder._wait_for_batch(submission)
except RuntimeError as e:
msg = str(e)
if 'ended as canceled' in msg:
handle_cancel(submission)
elif 'ended as partial' in msg:
resubmit_failed_items(submission) # parse failed count from msg / item statuses
else:
raise Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Zep batch {submission.batch_id} did not finish within {timeo
- Zep batch {submission.batch_id} contains {len(items)} items,
- Zep batch {submission.batch_id} returned an incomplete item
- Zep batch {batch_id} item submission is unconfirmed; the dra
- Zep batch {batch_id} acknowledged {len(item_details or [])}
AI-assisted analysis of 666ghj/MiroFish@b5b53acc57 (2026-08-14).
Data as JSON: /api/errors/be8a33b97a6abc84.
Report an issue: GitHub.