deepseek-ai/deepseek-harness · warning
approval response rejected: ${receipt.reason}
Error message
approval response rejected: ${receipt.reason} What it means
PendingApproval.answer() delivers the user's allowed-once/rejected decision through the carrier wait's respond(). The host reconciles approval responses against its pending set; if the response arrives too late or duplicates another answer, the receipt comes back accepted:false with a reason, which answer() rethrows as 'approval response rejected: <reason>'. Panel removal stays frame-driven via the broadcast approval/resolved event.
Source
Thrown at packages/client/ui-conversation/src/client/contract/slots.ts:720
/** The paired tool call's id when the ask names one (command-line lookup key), forwarded from the carrier payload. */
get callId(): string | undefined {
return this.wait.payload.callId
}
/**
* Deliver the user's decision; a rejected carrier receipt throws. Panel
* removal stays frame-driven: the broadcast `approval/resolved` settles the
* wait and drops it from the pending list.
* @param outcome - the only two client-answerable outcomes.
*/
async answer(outcome: 'allowed-once' | 'rejected'): Promise<void> {
const receipt = await this.wait.respond({
ok: true,
value: { sessionId: this.wait.sessionId, approvalId: this.wait.payload.approvalId, outcome },
})
if (!receipt.accepted) {
throw new Error(`approval response rejected: ${receipt.reason}`)
}
}
}
/**
* Full approval-composer props: the framework runtime share (chain currency +
* session/global standard kit) plus the chain `matched` share — the entry's
* selector result, already narrowed to the approval carrier — plus the
* standard locale seat. No injected share: the carrier plus the domain face
* above carry the whole behavior surface; the paired command line derives
* from useSession in-component.
*/
export type ApprovalComposerProps =
PropsRuntime<'conversation.composer'> & { matched: ApprovalWait } & PropsLocale<'conversation'>
/** In-memory reader position resilient to transcript width reflow. */
export interface ChatScrollPosition {
/** Stable rendered node/call identity nearest the visible reading edge. */View on GitHub (pinned to b150a551b8)
Solutions
- Treat as benign when the approval already resolved — check the pending list or the approval/resolved event before reporting an error
- Latch the Allow/Reject buttons one-shot so a second click cannot fire answer() twice
- If the reason says the wait never existed, resync session state to rebuild the pending approvals view
Defensive patterns
Strategy: try-catch
Try / catch
try {
await approval.answer(outcome)
} catch (error) {
if (approvalAlreadyResolved(approval.key)) return // benign double-answer race
throw error
} Prevention
- Latch Allow/Reject buttons one-shot so a second click cannot fire answer() twice
- Drop the panel when the approval/resolved broadcast lands, before processing further clicks
- Never auto-retry answer() after a rejected receipt — resync pending state instead
When it happens
Trigger: Answering an approval that was already settled elsewhere: double-clicking Allow/Reject, a second panel answering the same approvalId, the host or another client (CLI plus web) answering first, or the approval/resolved broadcast landing while the user's click is still in flight.
Common situations: Race between the resolved-event broadcast removing the panel and the user's click; retry logic that re-fires answer() after a timeout; multi-surface setups where either surface can decide.
Related errors
- conversation.sendSession: one or more draft images are no lo
- conversation.updateQueue failed: ${result.error.code}: ${res
- notice.imagesUnsupported
- conversation.serializeDraftImages: one or more draft images
- conversation.cancel failed: ${result.error.code}: ${result.e
AI-assisted analysis of deepseek-ai/deepseek-harness@b150a551b8 (2026-08-24).
Data as JSON: /api/errors/909b88475a98edac.
Report an issue: GitHub.