{"record":{"id":"806c87afbff5ca5e","repo":"paperclipai/paperclip","slug":"queued-comment-order-mismatch","errorCode":"queued_comment_order_mismatch","errorMessage":"The queued message order does not match the current queue","messagePattern":"The queued message order does not match the current queue","errorType":"error_code","errorClass":"QueuedCommentMutationError","httpStatus":null,"severity":"warning","filePath":"server/src/modules/wake-queue/application/queued-comment-use-cases.ts","lineNumber":171,"sourceCode":"  now: Date;\n};\n\nexport type ReorderQueuedCommentsResult = {\n  queue: QueuedCommentQueueSnapshot;\n  activityPublication: QueuedCommentActivityPublication;\n};\n\nexport function createReorderQueuedComments(deps: { issueLock: QueuedCommentIssueLockWriter }) {\n  return async function reorderQueuedComments(input: ReorderQueuedCommentsInput): Promise<ReorderQueuedCommentsResult> {\n    return deps.issueLock.withLockedQueue(\n      { issue: input.issue, actor: input.actor, queueId: input.queueId },\n      async (locked, tx) => {\n        requireMutationTarget(locked.queue, input.queueId, input.revision);\n\n        const currentIds = locked.queue.entries.map((entry) => entry.comment.id);\n        const reorderDecision = decideQueuedCommentReorder({ currentIds, orderedIds: input.orderedCommentIds });\n        if (reorderDecision.kind === \"mismatch\") {\n          throw new QueuedCommentMutationError(\n            \"queued_comment_order_mismatch\",\n            \"The queued message order does not match the current queue\",\n          );\n        }\n\n        const updatedWake = await tx.updateWakeQueuedCommentIds({\n          wakeId: locked.wake.id,\n          payload: locked.wake.payload,\n          ids: input.orderedCommentIds,\n          updatedAt: input.now,\n        });\n        const updatedQueueRun = await updateQueueRunCommentIdsGuarded(tx, {\n          queueRun: locked.queueRun,\n          ids: input.orderedCommentIds,\n          updatedAt: input.now,\n        });\n\n        const queue = await tx.buildQueueSnapshot({","sourceCodeStart":153,"sourceCodeEnd":189,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/server/src/modules/wake-queue/application/queued-comment-use-cases.ts#L153-L189","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nawait api.reorderQueuedComments({ issueId, queueId, revision: staleRevision, orderedCommentIds: cachedIds });\n// after\nconst queue = await api.getQueuedComments({ issueId });\nawait api.reorderQueuedComments({ issueId, queueId, revision: queue.revision, orderedCommentIds: queue.entries.map(e => e.comment.id) });","handlingStrategy":"validation","validationCode":"function canReorder(queue, orderedIds) {\n  const current = queue.entries.map(e => e.comment.id).sort();\n  const submitted = [...orderedIds].sort();\n  return queue.revision != null && current.length === submitted.length && current.every((id, i) => id === submitted[i]);\n}","typeGuard":"function isFreshQueueSnapshot(queue, revision) {\n  return typeof queue?.revision === \"string\" && queue.revision === revision;\n}","tryCatchPattern":"try {\n  await reorderQueuedComments(input);\n} catch (e) {\n  if (e instanceof QueuedCommentMutationError && e.code === \"queued_comment_order_mismatch\") {\n    await refreshQueue(); // reload snapshot + revision, let user re-apply\n  } else throw e;\n}","preventionTips":["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."],"tags":["concurrency","conflict","stale-state","optimistic-concurrency"],"backgroundTag":"invalid-state-transition","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-22T06:17:15.046Z"}