666ghj/MiroFish · error · TimeoutError

Zep episode processing timed out with {len(pending_episodes)

Error message

Zep episode processing timed out with {len(pending_episodes)} episode(s) still pending

What it means

Raised by GraphBuilder._wait_for_episodes when polling client.graph.episode.get(uuid_=...) shows episodes still not processed (episode.processed is falsy) after the timeout window. After a batch upload, Zep asynchronously processes each episode into graph entities/edges; this loop blocks until all episode_uuids report processed=True or the deadline (default derived from ZEP_INGESTION_WAIT_TIMEOUT_SECONDS = 600s) expires, then raises TimeoutError.

Source

Thrown at backend/app/services/graph_builder.py:747

                progress_callback(t('progress.noEpisodesWait'), 1.0)
            return
        
        start_time = time.time()
        pending_episodes = set(episode_uuids)
        completed_count = 0
        total_episodes = len(episode_uuids)
        
        if progress_callback:
            progress_callback(t('progress.waitingEpisodes', count=total_episodes), 0)
        
        while pending_episodes:
            if time.time() - start_time > timeout:
                if progress_callback:
                    progress_callback(
                        t('progress.episodesTimeout', completed=completed_count, total=total_episodes),
                        completed_count / total_episodes
                    )
                raise TimeoutError(
                    f"Zep episode processing timed out with "
                    f"{len(pending_episodes)} episode(s) still pending"
                )
            
            # 检查每个 episode 的处理状态
            for ep_uuid in list(pending_episodes):
                episode = call_zep_read_with_retry(
                    lambda: self.client.graph.episode.get(uuid_=ep_uuid),
                    operation_name=f"poll episode {ep_uuid}",
                )
                is_processed = getattr(episode, 'processed', False)

                if is_processed:
                    pending_episodes.remove(ep_uuid)
                    completed_count += 1
            
            elapsed = int(time.time() - start_time)
            if progress_callback:

View on GitHub (pinned to b5b53acc57)

Solutions

  1. Increase the episode wait timeout proportional to episode count (graph extraction is far slower than batch acceptance).
  2. Re-poll the pending episodes after the timeout — most complete shortly after; only treat permanently unprocessed episodes as failures.
  3. Ingest in smaller batches so the per-batch episode queue stays shallow.
  4. If specific episode UUIDs stay unprocessed for hours, escalate to Zep support with the UUIDs.

Example fix

# before
builder._wait_for_episodes(episode_uuids, timeout=600)
# after
timeout = max(600, 30 * len(episode_uuids))
builder._wait_for_episodes(episode_uuids, timeout=timeout)
Defensive patterns

Strategy: retry

Validate before calling

timeout = max(600, 30 * len(episode_uuids))  # graph extraction is slower than batch acceptance

Try / catch

try:
    builder._wait_for_episodes(episode_uuids, timeout=timeout)
except TimeoutError:
    still_pending = [u for u in episode_uuids if poll_later(u)]
    if still_pending:
        escalate(still_pending)
    # else: all completed shortly after the deadline; continue

Prevention

When it happens

Trigger: Many episodes queued for graph processing at once (large batch succeeded, graph extraction backlog exceeds the wait window); Zep cloud under load; a few episodes stuck unprocessed indefinitely on Zep's side; timeout passed too small by the caller.

Common situations: Ingesting hundreds of chunks in one go and immediately waiting for graph readiness; peak-hour Zep latency; following a batch that 'succeeded' quickly but whose graph extraction is slow.

Understand the failure class

Related errors


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