666ghj/MiroFish · error · RuntimeError
Multiple Zep batches match operation {operation_id}; refusin
Error message
Multiple Zep batches match operation {operation_id}; refusing ambiguity What it means
RuntimeError in _find_batch_by_operation_id: the paginated batch list found two or more Zep batches whose metadata carries the same mirofish_operation_id (and graph_id). Because a batch-create call is not idempotent, a replayed create after an ambiguous timeout can produce a duplicate batch; rather than guessing which one holds the canonical data, the service refuses and asks for manual disambiguation.
Source
Thrown at backend/app/services/graph_builder.py:304
operation_name=f"reconcile batch create {operation_id}",
)
for batch in getattr(page, "batches", None) or []:
metadata = getattr(batch, "metadata", None) or {}
if (
metadata.get("mirofish_operation_id") == operation_id
and metadata.get("graph_id") == graph_id
):
matches.append(batch)
next_cursor = getattr(page, "next_cursor", None)
if next_cursor is None:
break
if next_cursor == cursor or next_cursor in seen_cursors:
raise RuntimeError("Zep batch list cursor did not advance")
seen_cursors.add(next_cursor)
cursor = next_cursor
if len(matches) > 1:
raise RuntimeError(
f"Multiple Zep batches match operation {operation_id}; refusing ambiguity"
)
if matches:
return matches[0]
if attempt < max_attempts:
time.sleep(attempt)
return None
def set_ontology(self, graph_id: str, ontology: Dict[str, Any]):
"""设置图谱本体(公开方法)"""
import warnings
from typing import Optional
from pydantic import Field
from zep_cloud.external_clients.ontology import EntityModel, EntityText, EdgeModel
# 抑制 Pydantic v2 关于 Field(default=None) 的警告
# 这是 Zep SDK 要求的用法,警告来自动态类创建,可以安全忽略
warnings.filterwarnings('ignore', category=UserWarning, module='pydantic')View on GitHub (pinned to b5b53acc57)
Solutions
- Inspect all batches with this operation_id in the Zep Cloud console (metadata mirofish_operation_id) and compare item counts/status.
- Delete the stale/draft duplicate batch in Zep, keep the processed one, then retry the build so the reconcile finds a single match.
- If both are partial drafts, delete both and restart the build fresh for that operation.
- Prevent recurrence: always clear project.zep_batch_id when abandoning a build, and never re-submit create for an operation whose batch may already exist.
- If concurrent submissions are possible, serialize build submission per project (the graph_lifecycle_lock pattern) so the same operation_id is never in flight twice.
Example fix
# before
if len(matches) > 1:
raise RuntimeError(f"Multiple Zep batches match operation {operation_id}; refusing ambiguity")
# after - prefer the processed batch deterministically, still refuse true ties
if len(matches) > 1:
processed = [b for b in matches if getattr(b, "status", None) not in {None, "draft"}]
drafts = [b for b in matches if b not in processed]
if len(processed) == 1:
for d in drafts:
self._safe_delete_batch(getattr(d, "batch_id", None))
matches = processed
else:
raise RuntimeError(
f"Multiple Zep batches match operation {operation_id} "
f"(ids={[getattr(b, 'batch_id', None) for b in matches]}); refusing ambiguity"
) Defensive patterns
Strategy: validation
Validate before calling
matches = [b for b in list_all_batches() if (b.metadata or {}).get('mirofish_operation_id') == operation_id]
if len(matches) > 1:
# operator must pick: prefer processed, delete drafts; do not auto-choose blindly
raise AmbiguousBatchError([b.batch_id for b in matches]) Try / catch
try:
submission = builder.submit_document_batch(graph_id, chunks, batch_size=bs)
except RuntimeError as e:
if 'refusing ambiguity' in str(e):
# inspect + clean duplicates in Zep console, then restart the build fresh
logger.error('Duplicate Zep batches detected: %s', str(e))
raise
raise Prevention
- Never re-POST batch.create for an operation whose create outcome is uncertain; always reconcile by metadata first.
- Clear project.zep_batch_id/operation_id when abandoning a build so stale batches are not resumed.
- Serialize build submissions per project so the same operation_id is never in flight twice.
- Clean up draft batches in Zep after failed runs before starting new ones.
When it happens
Trigger: A retryable error on client.batch.create triggers _find_batch_by_operation_id, and the list now shows the original batch plus a duplicate created by an earlier replay; two concurrent build submissions computed the same operation_id; leftover batches from a previous failed run with identical content (same graph_id + chunks hash to the same operation_id).
Common situations: Network flakiness causing the create to be issued twice; resuming a build after a crash where the previous batch was never cleaned up; running the same document against the same graph twice and the old batch was not deleted.
Related errors
- Persisted Zep batch does not match the current graph input
- Zep batch creation is unconfirmed and no matching operation
- Zep Batch API returned no batch_id
- Zep batch {batch_id} item submission is unconfirmed; the dra
- Zep batch {batch_id} item submission failed
AI-assisted analysis of 666ghj/MiroFish@b5b53acc57 (2026-08-14).
Data as JSON: /api/errors/7b890e1605fe7616.
Report an issue: GitHub.