can1357/oh-my-pi · error
RPC message snapshot does not match current messages
Error message
RPC message snapshot does not match current messages
What it means
pageRpcMessages compares snapshot.messageCount with the actual messages array length; they must match because the cursor pins a specific stable snapshot. This error means the message list changed between when the snapshot was taken and when the page was requested, so the snapshot is no longer valid.
Source
Thrown at packages/coding-agent/src/modes/rpc/rpc-messages.ts:99
return { version, sessionId, leafId, messageCount, offset };
}
function sameSnapshot(cursor: RpcMessageCursorPayload, snapshot: RpcMessageSnapshot): boolean {
return (
cursor.sessionId === snapshot.sessionId &&
cursor.leafId === snapshot.leafId &&
cursor.messageCount === snapshot.messageCount
);
}
/** Page one stable in-memory message snapshot without crossing the v1 frame budget. */
export function pageRpcMessages(
messages: readonly AgentMessage[],
snapshot: RpcMessageSnapshot,
options: RpcMessagesPageOptions = {},
): RpcMessagesPage {
if (snapshot.messageCount !== messages.length)
throw new Error("RPC message snapshot does not match current messages");
const limit = options.limit ?? DEFAULT_RPC_MESSAGE_PAGE_LIMIT;
if (!Number.isSafeInteger(limit) || limit < 1 || limit > MAX_RPC_MESSAGE_PAGE_LIMIT)
throw new Error(`RPC message page limit must be between 1 and ${MAX_RPC_MESSAGE_PAGE_LIMIT}`);
let offset = 0;
if (options.cursor !== undefined) {
const cursor = decodeCursor(options.cursor);
if (!sameSnapshot(cursor, snapshot))
throw new RpcMessagesPageError(RPC_MESSAGES_PAGE_STALE_ERROR, "stale_cursor");
offset = cursor.offset;
}
const page: AgentMessage[] = [];
let pageBytes = 2;
while (offset + page.length < messages.length && page.length < limit) {
const message = messages[offset + page.length];
const messageBytes = Buffer.byteLength(JSON.stringify(message), "utf8") + (page.length === 0 ? 0 : 1);
if (page.length > 0 && pageBytes + messageBytes > MAX_RPC_MESSAGE_PAGE_BYTES) break;
page.push(message);View on GitHub (pinned to 9690622007)
Solutions
- Wait until the session is idle (agent_end / willContinue false) before paging messages.
- Re-fetch a fresh snapshot (and discard old cursors) whenever the message count changes — cursors are snapshot-bound and not valid across snapshots.
- Catch this error, refresh snapshot + messages, and retry the page request against the new snapshot.
- Use the structured error handling path: this raw Error indicates a caller bug/race, unlike RpcMessagesPageError which has a wire code — fix the calling code to snapshot and page atomically.
Example fix
// before
const snapshot = { sessionId, leafId, messageCount: messages.length };
await runAgent(); // messages grows
page(messages, snapshot); // mismatch
// after
await runAgent();
const snapshot = { sessionId, leafId, messageCount: messages.length };
page(messages, snapshot); // snapshot taken after messages stabilized Defensive patterns
Strategy: try-catch
Validate before calling
function snapshotMatches(messages: readonly unknown[], snapshot: RpcMessageSnapshot): boolean {
return snapshot.messageCount === messages.length;
}
if (!snapshotMatches(messages, snapshot)) {
({ messages, snapshot } = refreshSnapshot()); // re-capture before paging
} Try / catch
try {
return pageRpcMessages(messages, snapshot, opts);
} catch (err) {
if (err.message === "RPC message snapshot does not match current messages") {
const fresh = getFreshSnapshotAndMessages();
return pageRpcMessages(fresh.messages, fresh.snapshot, {}); // old cursor is void
}
throw err;
} Prevention
- Page only while the session is idle (after agent_end)
- Take the snapshot and call page atomically from the same messages array
- Discard cursors whenever the message count changes — they are bound to one snapshot
When it happens
Trigger: Calling get_messages/page while the agent is actively appending messages so messages.length differs from the snapshot's messageCount; reusing an old snapshot with a mutated message list; concurrent requests during an agent run.
Common situations: A client pages a live session mid-run instead of waiting for agent_end; the caller built the snapshot once but passed a different messages array; a race between snapshot capture and paging in multi-request clients.
Related errors
- RPC message pagination returned an inconsistent total
- OAuth refresh ownership was lost before persistence
- No credential with id=${id}
- session header changed during stats cleanup: ${session.path}
- Daemon ${spec.name} is already starting
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/8d7d6cf82a2cb60c.
Report an issue: GitHub.