block/buzz · error · Error

Could not load active huddles: pagination cursor did not adv

Error message

Could not load active huddles: pagination cursor did not advance.

What it means

fetchHuddleLifecycleHistory pages backward through huddle lifecycle history with a (until, beforeId) cursor. If a full page's terminal (oldest) event is identical to the current cursor, pagination can never advance and the loop would spin forever, so it throws this error as a safety valve.

Source

Thrown at desktop/src/features/huddle/lib/huddlePresence.ts:240

      ],
      ...(channelIds?.length ? { "#h": channelIds } : {}),
      ...(until === undefined ? {} : { until }),
      limit: HUDDLE_LIFECYCLE_PAGE_LIMIT,
      ...(beforeId === undefined ? {} : { before_id: beforeId }),
    });
    for (const event of page) events.set(event.id, event);
    if (page.length < HUDDLE_LIFECYCLE_PAGE_LIMIT) break;

    const terminal = [...page]
      .sort((left, right) =>
        left.created_at !== right.created_at
          ? right.created_at - left.created_at
          : left.id.localeCompare(right.id),
      )
      .at(-1);
    if (!terminal) break;
    if (until === terminal.created_at && beforeId === terminal.id) {
      throw new Error(
        "Could not load active huddles: pagination cursor did not advance.",
      );
    }
    until = terminal.created_at;
    beforeId = terminal.id;
  }

  return [...events.values()];
}

/** Incremental, bounded reconstruction of authenticated active huddles. */
export class HuddlePresenceTracker {
  private readonly relaySelf: string;
  private readonly sessions = new Map<string, HuddleSession>();

  constructor(relaySelfPubkey: string | null | undefined) {
    this.relaySelf = normalizePubkey(relaySelfPubkey ?? "");
  }

View on GitHub (pinned to dad5a33865)

Solutions

  1. Check the relay version honors NIP-01 'until' and 'before' filter parameters; upgrade the relay if pagination bounds are ignored.
  2. Inspect the events around the cursor for duplicate ids/created_at values and correct the ordering tiebreak (id comparison) in the query.
  3. Add a retry with a slightly adjusted cursor (e.g. until = terminal.created_at - 1) as a workaround for boundary ties.
  4. Reproduce with a direct REQ using the failing cursor and compare returned page boundaries.

Example fix

// before
if (until === terminal.created_at && beforeId === terminal.id) {
  throw new Error("Could not load active huddles: pagination cursor did not advance.");
}

// after
if (until === terminal.created_at && beforeId === terminal.id) {
  // one-time boundary nudge for ties, then fail if still stuck
  if (!nudged) { nudged = true; until = terminal.created_at - 1; beforeId = undefined; continue; }
  throw new Error("Could not load active huddles: pagination cursor did not advance.");
}
Defensive patterns

Strategy: retry

Try / catch

try {
  const huddles = await fetchHuddleLifecycleHistory(args);
} catch (e) {
  if (e instanceof Error && e.message.includes("pagination cursor did not advance")) {
    logger.warn("huddle history pagination stalled; rendering partial history", e);
    return partialResults; // degrade gracefully, don't block the UI
  }
  throw e;
}

Prevention

When it happens

Trigger: The relay returns a page whose oldest event equals the current cursor (same created_at and id), typically from a relay/DB that ignores or mishandles the 'until'/'before' filter bounds, or duplicate events across pages.

Common situations: Relay bug or older relay version that doesn't honor until/before filters; clock collisions where several events share created_at and the id tiebreak re-returns the same event; Postgres event-store ordering anomaly in the active-huddles query.

Related errors


AI-assisted analysis of block/buzz@dad5a33865 (2026-09-05). Data as JSON: /api/errors/a3b7af8fa80513e2. Report an issue: GitHub.