{"record":{"id":"7cb28dacec3f61a5","repo":"paperclipai/paperclip","slug":"the-queued-messages-changed-refresh-and-try-again","errorCode":null,"errorMessage":"The queued messages changed. Refresh and try again.","messagePattern":"The queued messages changed\\. Refresh and try again\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ui/src/api/issues.ts","lineNumber":394,"sourceCode":"      `/issues/${id}/queued-comments/${commentId}`,\n      data,\n    ),\n  reorderQueuedComments: (\n    id: string,\n    data: { orderedCommentIds: string[]; queueId: string; revision: string },\n  ) =>\n    api.put<IssueQueuedCommentQueue>(\n      `/issues/${id}/queued-comments/order`,\n      data,\n    ),\n  interruptQueuedComments: (\n    id: string,\n    data: { queueId: string; targetRunId: string | null; revision: string },\n  ) => api.post<IssueQueuedCommentQueue>(`/issues/${id}/queued-comments/interrupt`, data),\n  interruptLatestQueuedComments: async (id: string, expectedTargetRunId: string | null): Promise<IssueQueuedCommentQueue> => {\n    const queue = await issuesApi.getQueuedComments(id);\n    if (!queue.queueId || (queue.targetRunId && queue.targetRunId !== expectedTargetRunId)) {\n      throw new Error(\"The queued messages changed. Refresh and try again.\");\n    }\n    return issuesApi.interruptQueuedComments(id, {\n      queueId: queue.queueId, revision: queue.revision, targetRunId: queue.targetRunId,\n    });\n  },\n  steerQueuedComment: (\n    id: string,\n    commentId: string,\n    data: { queueId: string; targetRunId: string; revision: string },\n  ) =>\n    api.post<IssueQueuedCommentQueue>(\n      `/issues/${id}/queued-comments/${commentId}/steer`,\n      data,\n    ),\n  discardQueuedComment: (\n    id: string,\n    commentId: string,\n    data: { queueId: string; revision: string },","sourceCodeStart":376,"sourceCodeEnd":412,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/ui/src/api/issues.ts#L376-L412","documentation":"interruptLatestQueuedComments first fetches the current queued-comment queue and validates it against the caller's expectedTargetRunId before interrupting. If the queue is empty (no queueId) or its targetRunId differs from what the caller expected, it throws \"The queued messages changed. Refresh and try again.\" — an optimistic-concurrency pre-check to avoid interrupting the wrong queue.","triggerScenarios":"Calling issuesApi.interruptLatestQueuedComments(id, expectedTargetRunId) when the queue was drained, rotated to a new run (targetRunId changed after a run started/finished), or never existed — between composing the intent and the interrupt, another actor or a run transition changed it.","commonSituations":"User leaves the queued-messages panel open while the agent starts a new run so targetRunId advances; a second tab/user already interrupted the queue; run finished and the queue was cleared server-side; stale UI state after a long idle period.","solutions":["Refresh the issue to reload the current queue and retry the interrupt against the new targetRunId, exactly as the message says.","Re-fetch getQueuedComments and compare targetRunId; if it moved to a run you control, recompute the expected value and call again.","Handle this error in the UI by re-rendering the queued-messages panel instead of showing a dead-end error.","If queues change constantly, pause new queuing while the user edits rather than interrupting stale state."],"exampleFix":"// before\nawait issuesApi.interruptLatestQueuedComments(issueId, expectedRunId); // throws on mismatch\n// after\ntry {\n  await issuesApi.interruptLatestQueuedComments(issueId, expectedRunId);\n} catch (e) {\n  if (e.message.includes(\"queued messages changed\")) {\n    await refreshQueue(); // re-fetch queue, update UI, let user retry\n    return;\n  }\n  throw e;\n}","handlingStrategy":"validation","validationCode":"const queue = await issuesApi.getQueuedComments(issueId);\nconst canInterrupt = Boolean(queue.queueId) && (!queue.targetRunId || queue.targetRunId === expectedTargetRunId);\nif (!canInterrupt) await refreshQueueView(); // refresh instead of calling interrupt","typeGuard":"null","tryCatchPattern":"try {\n  await issuesApi.interruptLatestQueuedComments(issueId, expectedRunId);\n} catch (e) {\n  if (e.message.includes(\"queued messages changed\")) {\n    await refreshQueuedCommentsPanel();\n    return;\n  }\n  throw e;\n}","preventionTips":["Subscribe to queue/run updates (SSE/polling) so the panel's targetRunId stays fresh before interrupting.","Disable the interrupt button when the queue's targetRunId diverges from the run the user is looking at.","Prompt users to refresh when the queued-messages panel has been open longer than the run's lifetime."],"tags":["ui","concurrency","optimistic-locking"],"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-23T08:17:48.524Z"}