paperclipai/paperclip · error · Error
The queued messages changed. Refresh and try again.
Error message
The queued messages changed. Refresh and try again.
What it means
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.
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.
Example fix
// before
await issuesApi.interruptLatestQueuedComments(issueId, expectedRunId); // throws on mismatch
// after
try {
await issuesApi.interruptLatestQueuedComments(issueId, expectedRunId);
} catch (e) {
if (e.message.includes("queued messages changed")) {
await refreshQueue(); // re-fetch queue, update UI, let user retry
return;
}
throw e;
} Defensive patterns
Strategy: validation
Validate before calling
const queue = await issuesApi.getQueuedComments(issueId); const canInterrupt = Boolean(queue.queueId) && (!queue.targetRunId || queue.targetRunId === expectedTargetRunId); if (!canInterrupt) await refreshQueueView(); // refresh instead of calling interrupt
Type guard
null
Try / catch
try {
await issuesApi.interruptLatestQueuedComments(issueId, expectedRunId);
} catch (e) {
if (e.message.includes("queued messages changed")) {
await refreshQueuedCommentsPanel();
return;
}
throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
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_stale_queue
- stale_revision
- ACPX provider ownership admission is closed
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/7cb28dacec3f61a5.
Report an issue: GitHub.
Appendix: source
Thrown at ui/src/api/issues.ts:394
`/issues/${id}/queued-comments/${commentId}`,
data,
),
reorderQueuedComments: (
id: string,
data: { orderedCommentIds: string[]; queueId: string; revision: string },
) =>
api.put<IssueQueuedCommentQueue>(
`/issues/${id}/queued-comments/order`,
data,
),
interruptQueuedComments: (
id: string,
data: { queueId: string; targetRunId: string | null; revision: string },
) => api.post<IssueQueuedCommentQueue>(`/issues/${id}/queued-comments/interrupt`, data),
interruptLatestQueuedComments: async (id: string, expectedTargetRunId: string | null): Promise<IssueQueuedCommentQueue> => {
const queue = await issuesApi.getQueuedComments(id);
if (!queue.queueId || (queue.targetRunId && queue.targetRunId !== expectedTargetRunId)) {
throw new Error("The queued messages changed. Refresh and try again.");
}
return issuesApi.interruptQueuedComments(id, {
queueId: queue.queueId, revision: queue.revision, targetRunId: queue.targetRunId,
});
},
steerQueuedComment: (
id: string,
commentId: string,
data: { queueId: string; targetRunId: string; revision: string },
) =>
api.post<IssueQueuedCommentQueue>(
`/issues/${id}/queued-comments/${commentId}/steer`,
data,
),
discardQueuedComment: (
id: string,
commentId: string,
data: { queueId: string; revision: string },View on GitHub (pinned to 3f1d897a7c)