paperclipai/paperclip · error
paperclip_current_wake_comments_cursor_invalid
paperclip_current_wake_comments_cursor_invalid
Error message
paperclip_current_wake_comments_cursor_invalid
What it means
decodeCursor validates the opaque base64url pagination cursor for the current wake comments endpoint. It throws when the cursor is not a non-empty string or exceeds MAX_CURSOR_CHARS, before even attempting to decode it. This is the first of several validation gates that all use the same error code.
Solutions
- Omit the cursor parameter entirely for the first page instead of sending an empty string.
- Use the cursor value exactly as returned by the API, unmodified (no re-encoding or truncation).
- Check cursor length against MAX_CURSOR_CHARS before sending; re-fetch page 1 if the stored cursor is too long.
- Ensure the cursor came from the same endpoint's prior response, not a different comments API.
Example fix
// before
const url = `/api/comments?cursor=${cursor ?? ""}`;
// after
const url = cursor ? `/api/comments?cursor=${encodeURIComponent(cursor)}` : "/api/comments"; Defensive patterns
Strategy: validation
Validate before calling
const cursorOk = typeof cursor === "string" && cursor.length > 0 && cursor.length <= 4096;
if (cursor && !cursorOk) throw new Error("client: cursor must be a non-empty, unmodified string"); Type guard
const isUsableCursor = (c: unknown): c is string => typeof c === "string" && c.length > 0 && c.length <= 4096;
Try / catch
try { return await fetchComments({ cursor }); } catch (e) { if (e.message.includes("cursor_invalid")) return fetchComments({}); // restart from page 1
throw e; } Prevention
- Omit the cursor param for the first page instead of sending an empty string.
- Echo cursors back verbatim with no re-encoding, padding, or truncation.
- Persist cursors only from the same endpoint that issued them.
When it happens
Trigger: Passing cursor=undefined/null coerced into a string call path that skips the null early-return, an empty string cursor, or a cursor string longer than MAX_CURSOR_CHARS (client-supplied garbage or oversized cursor from another endpoint).
Common situations: Clients sending `?cursor=` (empty query param) instead of omitting it; storing cursors that were padded/altered in transit (URL encoding, truncation by logs or proxies); frontends persisting a cursor from a different endpoint with a larger payload.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- codex_history_invalid_cursor
- invalid
- paperclip_runner_chat_attachment_cursor_invalid
- A full lowercase source SHA is required.
- A reusable lease cannot be replaced and reacquired in the…
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/1f9dafbab36cd769.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/services/native-runtime/current-wake-comments.ts:355
};
}
function encodeCursor(cursor: CurrentWakeCommentsCursor): string {
return Buffer.from(JSON.stringify(cursor), "utf8").toString("base64url");
}
function decodeCursor(
value: unknown,
binding: CurrentWakeCommentsBinding,
snapshotDigest: string,
): CurrentWakeCommentsCursor | null {
if (value === undefined || value === null) return null;
if (
typeof value !== "string" ||
value.length === 0 ||
value.length > MAX_CURSOR_CHARS
) {
throw new Error("paperclip_current_wake_comments_cursor_invalid");
}
try {
const parsed = record(
JSON.parse(Buffer.from(value, "base64url").toString("utf8")),
);
if (
parsed.schema !== "paperclip.current-wake-comments-cursor.v1" ||
parsed.bindingDigest !== binding.bindingDigest ||
parsed.snapshotDigest !== snapshotDigest ||
!Number.isSafeInteger(parsed.commentIndex) ||
!Number.isSafeInteger(parsed.bodyOffset) ||
Number(parsed.commentIndex) < 0 ||
Number(parsed.bodyOffset) < 0
) {
throw new Error("paperclip_current_wake_comments_cursor_invalid");
}
return parsed as CurrentWakeCommentsCursor;
} catch (error) {View on GitHub (pinned to 3f1d897a7c)