paperclipai/paperclip · error

Discard is unavailable.

Error message

Discard is unavailable.

What it means

In TaskChatThread's onDiscard handler there are two failure paths that throw "Discard is unavailable." This instance (line 2728) fires for comments whose id starts with the "optimistic-" prefix: those are local, not-yet-acknowledged queue entries that can only be removed via the onCancelQueued callback. If that prop was not supplied, the component throws rather than attempting to discard a comment that does not exist server-side.

Source

Thrown at ui/src/components/TaskChatThread.tsx:2728

                  }}
                  onSteer={async (commentId, revision) => {
                    if (!onSteerQueuedComment)
                      throw new Error("Steering is unavailable.");
                    await onSteerQueuedComment(commentId, revision);
                  }}
                  onInterrupt={
                    onInterruptQueued && queuedMessageQueue.targetRunId
                      ? async () => {
                          await onInterruptQueued(
                            queuedMessageQueue.targetRunId!,
                          );
                        }
                      : undefined
                  }
                  onDiscard={async (commentId, revision) => {
                    if (commentId.startsWith("optimistic-")) {
                      if (!onCancelQueued)
                        throw new Error("Discard is unavailable.");
                      onCancelQueued(commentId);
                      return;
                    }
                    if (!onDiscardQueuedComment)
                      throw new Error("Discard is unavailable.");
                    await onDiscardQueuedComment(commentId, revision);
                    if (queuedEdit?.commentId === commentId)
                      setQueuedEdit(null);
                  }}
                />
              ) : null}
              <div className="relative z-10">
                <TaskChatComposer
                  onAdd={handleThreadAdd}
                  onStop={liveRun ? onCancelRun : undefined}
                  stopPending={stopPending}
                  stopScope={stopScope}
                  workMode={issueWorkMode}

View on GitHub (pinned to 01ad858492)

Solutions

  1. Pass the onCancelQueued prop to TaskChatThread at the call site.
  2. Ensure the parent's optimistic queue-add path also wires the cancel handler symmetrically.
  3. If optimistic queuing is not needed, render the queue without optimistic ids so discard goes through onDiscardQueuedComment.
  4. Guard the discard button to only render when the matching cancel/discard callback exists.

Example fix

// before
<TaskChatThread issueId={id} onDiscardQueuedComment={discard} />

// after
<TaskChatThread
  issueId={id}
  onDiscardQueuedComment={discard}
  onCancelQueued={(optimisticId) => removeOptimisticComment(optimisticId)}
/>
Defensive patterns

Strategy: validation

Validate before calling

const canCancelOptimistic =
  commentId.startsWith("optimistic-") && typeof onCancelQueued === "function";
if (commentId.startsWith("optimistic-") && !canCancelOptimistic) disableDiscardButton();

Type guard

const isOptimistic = (id: string) => id.startsWith("optimistic-");
const hasCancel = (p: unknown): p is (id: string) => void => typeof p === "function";

Try / catch

try {
  await onDiscard(commentId, revision);
} catch (e) {
  if (e instanceof Error && e.message === "Discard is unavailable.") {
    toast("Discarding is not wired up for this view.");
  } else throw e;
}

Prevention

When it happens

Trigger: User discards a queued message that was added optimistically (id starts with "optimistic-") while the onCancelQueued prop is undefined in the embedding TaskChatThread.

Common situations: Custom embed of TaskChatThread without optimistic-cancel wiring; a parent that wires onDiscardQueuedComment but not onCancelQueued; stale optimistic entries from a failed queue post being discarded before server confirmation.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/d49bb5f7e8d64216. Report an issue: GitHub.