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

  1. Retry the full fetch from page one — cursor state is per-call and transient stalls usually clear.
  2. Stop concurrent writers (updaters, batch.process) before running full-graph fetches.
  3. Upgrade the zep-cloud package to the latest version — cursor handling has changed across releases.
  4. 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

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


AI-assisted analysis of 666ghj/MiroFish@b5b53acc57 (2026-08-14). Data as JSON: /api/errors/e638fab599b9174c. Report an issue: GitHub.