666ghj/MiroFish · error · RuntimeError
Zep {item_name} pagination cursor did not advance for graph
Error message
Zep {item_name} pagination cursor did not advance for graph {graph_id} What it means
Raised by the generic pagination loop in backend/app/utils/zep_paging.py when the zep-next-cursor response header (read via _header_value) either equals the current cursor or was already seen in seen_cursors. This is an infinite-loop guard: without it the fetch would request the same page forever. It is a RuntimeError with an f-string message naming the item kind (node/edge) and graph_id, raised after at least one successful page request. Used by fetch_all_nodes / fetch_all_edges.
Source
Thrown at backend/app/utils/zep_paging.py:112
if max_items is not None and len(all_items) >= max_items:
if len(all_items) > max_items:
all_items = all_items[:max_items]
logger.warning(
"Zep %s pagination reached explicit max_items=%s for graph %s",
item_name,
max_items,
graph_id,
)
break
next_cursor = _header_value(
getattr(response, "headers", None),
_NEXT_CURSOR_HEADER,
)
if next_cursor is None:
break
if next_cursor in seen_cursors or next_cursor == cursor:
raise RuntimeError(
f"Zep {item_name} pagination cursor did not advance for graph {graph_id}"
)
seen_cursors.add(next_cursor)
cursor = next_cursor
return all_items
def fetch_all_nodes(
client: Zep,
graph_id: str,
page_size: int = _DEFAULT_PAGE_SIZE,
max_items: int | None = None,
max_retries: int = _DEFAULT_MAX_RETRIES,
retry_delay: float = _DEFAULT_RETRY_DELAY,
) -> list[Any]:
"""Fetch every graph node unless the caller supplies an explicit cap."""
View on GitHub (pinned to b5b53acc57)
Solutions
- Retry the full fetch from page one — cursor state is per-call and transient stalls usually clear.
- Stop concurrent writers (updaters, batch.process) before running full-graph fetches.
- Upgrade the zep-cloud package to the latest version — cursor handling has changed across releases.
- If reproducible on a quiesced graph, capture the raw headers and report to Zep support with the graph_id.
Defensive patterns
Strategy: retry
Try / catch
for attempt in range(3):
try:
items = fetch_all_nodes(client, graph_id, page_size=50)
break
except RuntimeError as e:
if "cursor did not advance" not in str(e) or attempt == 2:
raise
time.sleep(2 ** attempt) # restart from page 1 Prevention
- Quiesce the graph before full fetches: stop updaters/batch writers first.
- Use larger page_size to reduce cursor transitions (fewer chances to observe a loop).
- Pin a known-good zep-cloud version and re-test pagination after SDK upgrades.
When it happens
Trigger: Calling fetch_all_nodes or fetch_all_edges on a graph whose API responses repeat a zep-next-cursor value — server-side pagination anomaly, a proxy rewriting/duplicating headers, or concurrent graph mutation between pages making cursors unstable.
Common situations: Zep Cloud API behavior change (cursor format or reuse); stale zep-cloud SDK version mis-parsing the header; paginating while an updater/batch job is actively writing to the same graph.
Related errors
- batch item cursor did not advance
- Zep batch list cursor did not advance
- Zep batch {batch_id} item cursor did not advance
- artifact cursor did not advance
- graph.add returned no episode UUID
AI-assisted analysis of 666ghj/MiroFish@b5b53acc57 (2026-08-14).
Data as JSON: /api/errors/e638fab599b9174c.
Report an issue: GitHub.