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

  1. Refresh the issue to reload the current queue and retry the interrupt against the new targetRunId, exactly as the message says.
  2. Re-fetch getQueuedComments and compare targetRunId; if it moved to a run you control, recompute the expected value and call again.
  3. Handle this error in the UI by re-rendering the queued-messages panel instead of showing a dead-end error.
  4. 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

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


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)