{"record":{"id":"ed747167a1e12fe2","repo":"mastra-ai/mastra","slug":"cannot-decline-a-tool-call-without-a-current-threa","errorCode":null,"errorMessage":"Cannot decline a tool call without a current thread","messagePattern":"Cannot decline a tool call without a current thread","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/agent-controller/session.ts","lineNumber":3797,"sourceCode":"    toolCallId,\n    requestContext: requestContextInput,\n    declineContext,\n  }: {\n    toolCallId?: string;\n    requestContext?: RequestContext;\n    declineContext?: { reason?: string; message?: string };\n  }): Promise<void> {\n    const runId = this.run.getRunId();\n    if (!runId) {\n      throw new Error('No active run to decline tool call for');\n    }\n\n    const agent = this.machinery.getAgent();\n    const requestContext = await this.machinery.buildRequestContext(requestContextInput);\n    const isYolo = (this.state.get() as Record<string, unknown>).yolo === true;\n    const threadId = this.thread.getId();\n    if (!threadId) {\n      throw new Error('Cannot decline a tool call without a current thread');\n    }\n    const resourceId = this.identity.getResourceId();\n    await agent.sendToolApproval({\n      threadId,\n      resourceId,\n      runId,\n      toolCallId,\n      approved: false,\n      declineContext,\n      requireToolApproval: !isYolo,\n      memory: { thread: threadId, resource: resourceId },\n      abortSignal: this.run.ensureAbortController().signal,\n      requestContext,\n      toolsets: await this.machinery.buildToolsets(requestContext),\n    });\n  }\n\n  private createSubscribedResumeBoundaryWaiter(toolCallId?: string): { promise: Promise<void>; cancel: () => void } {","sourceCodeStart":3779,"sourceCodeEnd":3815,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/core/src/agent-controller/session.ts#L3779-L3815","documentation":"Thrown when declining a tool call while the session has no current thread. The decline decision is persisted via `agent.sendToolApproval`, which requires a `threadId`; when `this.thread.getId()` returns undefined the library cannot record the decline and throws.","triggerScenarios":"Calling the decline-tool-call method on a session whose thread ID is unset — no thread created/selected, or thread state cleared before the decline call.","commonSituations":"Declining a tool call on a brand-new session with no conversation history; thread reset/cleanup racing with the decline; routing declines to a session instance different from the one owning the thread.","solutions":["Establish a current thread on the session (start the run or explicitly select/create the thread) before declining.","Check that cleanup logic isn't clearing the thread while a tool approval is pending.","Route the decline through the same session/thread that issued the tool call request."],"exampleFix":"// before\nawait session.declineToolCall({ toolCallId });\n\n// after\nconst threadId = session.getCurrentThreadId?.();\nif (!threadId) throw new Error('Select a thread before declining tool calls');\nawait session.declineToolCall({ toolCallId });","handlingStrategy":"validation","validationCode":"// before declining\nconst threadId = session.getCurrentThreadId?.();\nif (!threadId) {\n  throw new Error('A current thread is required to decline tool calls');\n}","typeGuard":"function hasThreadForDecline(session: { getCurrentThreadId?: () => string | undefined }): session is { getCurrentThreadId: () => string } {\n  return typeof session.getCurrentThreadId === 'function' && typeof session.getCurrentThreadId() === 'string';\n}","tryCatchPattern":"try {\n  await session.declineToolCall({ toolCallId });\n} catch (err) {\n  if (err instanceof Error && err.message.includes('without a current thread')) {\n    await session.selectThread(originalThreadId);\n    await session.declineToolCall({ toolCallId });\n  } else throw err;\n}","preventionTips":["Verify thread selection before enabling any tool-approval actions (approve or decline).","Avoid resetting threads mid-run; defer cleanup until pending decisions resolve.","Route decisions through the session that owns the originating thread."],"tags":["agent","tool-approval","thread"],"backgroundTag":"missing-thread-context","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}