paperclipai/paperclip · warning · QueuedCommentMutationError
queued_comment_order_mismatch
queued_comment_order_mismatch
Error message
The queued message order does not match the current queue
What it means
This error is thrown by createReorderQueuedComments when the caller-supplied orderedCommentIds do not correspond exactly to the set of comment IDs currently in the wake queue. The domain policy decideQueuedCommentReorder returns a 'mismatch' decision, meaning the reorder payload references a stale view of the queue. It maps 1:1 onto the HTTP 409 conflict the route previously returned, with code queued_comment_order_mismatch.
Solutions
- Refetch the current queue snapshot and re-derive orderedCommentIds from it, then retry the reorder with a fresh revision.
- Handle the 409/conflict code queued_comment_order_mismatch in the client by reloading the queue UI instead of retrying blindly.
- Ensure the client always submits the exact ID set from the latest queue.revision it holds; never merge ID lists from multiple snapshots.
- Remove filtering logic on the client that drops entries (e.g. hidden/collapsed messages) before sending the ID list — the payload must be a complete permutation of current IDs.
Example fix
// before
await api.reorderQueuedComments({ issueId, queueId, revision: staleRevision, orderedCommentIds: cachedIds });
// after
const queue = await api.getQueuedComments({ issueId });
await api.reorderQueuedComments({ issueId, queueId, revision: queue.revision, orderedCommentIds: queue.entries.map(e => e.comment.id) }); Defensive patterns
Strategy: validation
Validate before calling
function canReorder(queue, orderedIds) {
const current = queue.entries.map(e => e.comment.id).sort();
const submitted = [...orderedIds].sort();
return queue.revision != null && current.length === submitted.length && current.every((id, i) => id === submitted[i]);
} Type guard
function isFreshQueueSnapshot(queue, revision) {
return typeof queue?.revision === "string" && queue.revision === revision;
} Try / catch
try {
await reorderQueuedComments(input);
} catch (e) {
if (e instanceof QueuedCommentMutationError && e.code === "queued_comment_order_mismatch") {
await refreshQueue(); // reload snapshot + revision, let user re-apply
} else throw e;
} Prevention
- Always refetch the queue snapshot immediately before reordering and send its exact revision.
- Send the full ordered ID list — a complete permutation of current entries, never a subset or merged list.
- After any other queue mutation, invalidate cached queue state before reordering.
- Disable reorder controls while another mutation is in flight to avoid interleaved submissions.
When it happens
Trigger: Calling reorderQueuedComments (or the queue-discard/create use cases built from createReorderQueuedComments) with orderedCommentIds that: omit an existing queued comment, include a comment ID no longer in the queue (already dispatched, discarded, or deleted), or duplicate/misspell an ID. The revision check (requireMutationTarget) passed first, so the revision matched but the ID list still diverged.
Common situations: Two browser tabs both open on the issue queue: tab A reorders, tab B then submits its reorder with the pre-change ID list. A queued comment was dispatched between the client's snapshot fetch and the reorder submit. A client cached an old queue snapshot and reorders without refetching. Agent automation constructing orderedCommentIds from stale API responses.
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
- queued_comment_revision_conflict
- queued_comment_stale_queue
- stale_revision
- A different semantic result was already committed
- ACPX provider ownership admission is closed
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/806c87afbff5ca5e.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/modules/wake-queue/application/queued-comment-use-cases.ts:171
now: Date;
};
export type ReorderQueuedCommentsResult = {
queue: QueuedCommentQueueSnapshot;
activityPublication: QueuedCommentActivityPublication;
};
export function createReorderQueuedComments(deps: { issueLock: QueuedCommentIssueLockWriter }) {
return async function reorderQueuedComments(input: ReorderQueuedCommentsInput): Promise<ReorderQueuedCommentsResult> {
return deps.issueLock.withLockedQueue(
{ issue: input.issue, actor: input.actor, queueId: input.queueId },
async (locked, tx) => {
requireMutationTarget(locked.queue, input.queueId, input.revision);
const currentIds = locked.queue.entries.map((entry) => entry.comment.id);
const reorderDecision = decideQueuedCommentReorder({ currentIds, orderedIds: input.orderedCommentIds });
if (reorderDecision.kind === "mismatch") {
throw new QueuedCommentMutationError(
"queued_comment_order_mismatch",
"The queued message order does not match the current queue",
);
}
const updatedWake = await tx.updateWakeQueuedCommentIds({
wakeId: locked.wake.id,
payload: locked.wake.payload,
ids: input.orderedCommentIds,
updatedAt: input.now,
});
const updatedQueueRun = await updateQueueRunCommentIdsGuarded(tx, {
queueRun: locked.queueRun,
ids: input.orderedCommentIds,
updatedAt: input.now,
});
const queue = await tx.buildQueueSnapshot({View on GitHub (pinned to 3f1d897a7c)