mastra-ai/mastra · error

Cannot decline a tool call without a current thread

Error message

Cannot decline a tool call without a current thread

What it means

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.

Source

Thrown at packages/core/src/agent-controller/session.ts:3797

    toolCallId,
    requestContext: requestContextInput,
    declineContext,
  }: {
    toolCallId?: string;
    requestContext?: RequestContext;
    declineContext?: { reason?: string; message?: string };
  }): Promise<void> {
    const runId = this.run.getRunId();
    if (!runId) {
      throw new Error('No active run to decline tool call for');
    }

    const agent = this.machinery.getAgent();
    const requestContext = await this.machinery.buildRequestContext(requestContextInput);
    const isYolo = (this.state.get() as Record<string, unknown>).yolo === true;
    const threadId = this.thread.getId();
    if (!threadId) {
      throw new Error('Cannot decline a tool call without a current thread');
    }
    const resourceId = this.identity.getResourceId();
    await agent.sendToolApproval({
      threadId,
      resourceId,
      runId,
      toolCallId,
      approved: false,
      declineContext,
      requireToolApproval: !isYolo,
      memory: { thread: threadId, resource: resourceId },
      abortSignal: this.run.ensureAbortController().signal,
      requestContext,
      toolsets: await this.machinery.buildToolsets(requestContext),
    });
  }

  private createSubscribedResumeBoundaryWaiter(toolCallId?: string): { promise: Promise<void>; cancel: () => void } {

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Establish a current thread on the session (start the run or explicitly select/create the thread) before declining.
  2. Check that cleanup logic isn't clearing the thread while a tool approval is pending.
  3. Route the decline through the same session/thread that issued the tool call request.

Example fix

// before
await session.declineToolCall({ toolCallId });

// after
const threadId = session.getCurrentThreadId?.();
if (!threadId) throw new Error('Select a thread before declining tool calls');
await session.declineToolCall({ toolCallId });
Defensive patterns

Strategy: validation

Validate before calling

// before declining
const threadId = session.getCurrentThreadId?.();
if (!threadId) {
  throw new Error('A current thread is required to decline tool calls');
}

Type guard

function hasThreadForDecline(session: { getCurrentThreadId?: () => string | undefined }): session is { getCurrentThreadId: () => string } {
  return typeof session.getCurrentThreadId === 'function' && typeof session.getCurrentThreadId() === 'string';
}

Try / catch

try {
  await session.declineToolCall({ toolCallId });
} catch (err) {
  if (err instanceof Error && err.message.includes('without a current thread')) {
    await session.selectThread(originalThreadId);
    await session.declineToolCall({ toolCallId });
  } else throw err;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/ed747167a1e12fe2. Report an issue: GitHub.