paperclipai/paperclip · warning · QueuedCommentMutationError
queued_comment_stale_queue
queued_comment_stale_queue
Error message
The queued message targets a stale queue
What it means
requireMutationTarget guards queued-comment mutations (edit, reorder, discard) with optimistic concurrency checks. First it compares the caller's queueId against the current queue snapshot's queueId; if they differ, the message targets a queue generation that no longer exists, so it throws QueuedCommentMutationError code queued_comment_stale_queue. (A second check compares the revision for finer-grained conflicts.)
Solutions
- Refetch the current queue snapshot and retry the mutation with the new queueId and revision
- Handle code queued_comment_stale_queue in the UI by reloading the queue view and asking the user to re-apply the change
- Always source queueId/revision from the most recent GET of the queue, never cache across sessions
- Detect queue rotation events (wake completed, run changed) and invalidate cached queue handles
Example fix
// before await discardQueuedComment(staleQueueId, rev, commentId); // after const snap = await getQueue(agentId); await discardQueuedComment(snap.queueId, snap.revision, commentId);
Defensive patterns
Strategy: retry
Validate before calling
const snap = await getQueueSnapshot(agentId);
if (snap.queueId !== myQueueId) { await reloadQueue(); }
if (snap.revision !== myRevision) { await reloadQueue(); } // revision conflict is the sibling error Try / catch
try { await discardQueuedComment(queueId, rev, id); }
catch (e) {
if (e instanceof QueuedCommentMutationError && e.code === "queued_comment_stale_queue") {
const snap = await getQueueSnapshot(agentId);
await discardQueuedComment(snap.queueId, snap.revision, id); // one refresh+retry
} else throw e;
} Prevention
- Always pass queueId/revision from the most recent snapshot, never cached values
- Reload the queue view whenever a wake/run rotation may have occurred
- Handle queued_comment_stale_queue by refreshing, not blind-retrying
- Version-check the queue in the UI before rendering mutation controls
When it happens
Trigger: A client calls createEditQueuedComment / createReorderQueuedComments / createDiscardQueuedComment with a queueId that does not equal snapshot.queueId — the queue was recreated or rotated (e.g., after a wake, a config change, or a company/agent scope change) since the client fetched its snapshot.
Common situations: A long-lived UI tab holding an old queueId while the backend rebuilt the queue; an agent restart or heartbeat run change rotating the queue; concurrent sessions where one action invalidates the other's queue handle.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Discord correction draft ownership changed
- Photon state changed concurrently; retry
- queued_comment_already_dispatching
- queued_comment_not_pending
- queued_comment_not_pending
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/65511def5bd4e35c.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/modules/wake-queue/application/queued-comment-use-cases.ts:44
readonly code: QueuedCommentMutationErrorCode,
message: string,
) {
super(message);
this.name = "QueuedCommentMutationError";
}
}
/** The route maps this onto the `forbidden(...)` HTTP error it threw before this move, using `message` unchanged. */
export class QueuedCommentMutationForbiddenError extends Error {
constructor(message: string) {
super(message);
this.name = "QueuedCommentMutationForbiddenError";
}
}
function requireMutationTarget(queue: QueuedCommentQueueSnapshot, queueId: string, revision: string): void {
if (queue.queueId !== queueId) {
throw new QueuedCommentMutationError("queued_comment_stale_queue", "The queued message targets a stale queue");
}
if (queue.revision !== revision) {
throw new QueuedCommentMutationError("queued_comment_revision_conflict", "The queued messages changed in another session");
}
}
async function updateQueueRunCommentIdsGuarded(
tx: QueuedCommentQueueTransaction,
input: { queueRun: QueuedCommentRunRow | null; ids: string[]; updatedAt: Date },
): Promise<QueuedCommentRunRow | null> {
if (!input.queueRun) return null;
const updated = await tx.updateQueueRunCommentIds({
queueRunId: input.queueRun.id,
contextSnapshot: input.queueRun.contextSnapshot,
ids: input.ids,
updatedAt: input.updatedAt,
});
if (!updated) {View on GitHub (pinned to 3f1d897a7c)