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
- Pass the onCancelQueued prop to TaskChatThread at the call site.
- Ensure the parent's optimistic queue-add path also wires the cancel handler symmetrically.
- If optimistic queuing is not needed, render the queue without optimistic ids so discard goes through onDiscardQueuedComment.
- 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
- Wire onCancelQueued whenever optimistic queue entries are created.
- Pair every optimistic add with its cancel path in the same component.
- Disable discard for optimistic ids when no cancel handler exists.
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
- Steering is unavailable.
- Select an organization to test adapter environment
- [adapter-ui-loader] Failed to load UI parser for "${adapterT
- Failed to seed CEO instructions:
- [paperclip] UI dist not found; running in API-only mode
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/d49bb5f7e8d64216.
Report an issue: GitHub.