mastra-ai/mastra · error
Cannot resume a suspended tool without a current thread
Error message
Cannot resume a suspended tool without a current thread
What it means
Thrown while resuming a suspended tool when the session has no current thread. After the suspension is validated and removed from the registry, the resume flow needs a thread to re-subscribe to and continue the run within (`this.thread.ensureSubscription(threadId)`); a missing thread ID means the resumed run has no conversation context, so the library throws.
Source
Thrown at packages/core/src/agent-controller/session.ts:3872
}): Promise<void> {
const suspension = this.suspensions.get({ toolCallId });
if (!suspension) {
throw new Error('No active suspension to resume');
}
const agent = this.machinery.getAgent();
// Remove before resuming so a re-suspend during the resumed run can
// re-register the same toolCallId without being clobbered by this cleanup.
// Drop the matching display-state entry too so the UI stops rendering the
// resolved prompt while any other parked suspensions stay visible.
this.suspensions.delete({ toolCallId });
this.displayState.deletePendingSuspension(toolCallId);
const requestContext = await this.machinery.buildRequestContext(requestContextInput);
const threadId = this.thread.getId();
if (!threadId) {
throw new Error('Cannot resume a suspended tool without a current thread');
}
await this.thread.ensureSubscription(threadId);
const resumedSubscriptionBoundary = this.createSubscribedResumeBoundaryWaiter(
suspension.toolName === 'submit_plan' ? toolCallId : undefined,
);
try {
const resourceId = this.identity.getResourceId();
const sharedOptions = this.machinery.buildSharedRunOptions();
// Interactive builtins suspend to collect user input, not for approval.
// The resume data is the user's answer (a bare string), which the approval
// re-check would reject because it cannot carry an `{ approved }` field.
// Exempt these tools so the answer reaches the model as-is.
const isInteractive = suspension.toolName === 'ask_user' || suspension.toolName === 'request_access';
if (isInteractive) {
sharedOptions.requireToolApproval = false;
}View on GitHub (pinned to 75dd419e61)
Solutions
- Restore/select the original thread on the session before resuming the suspended tool.
- Ensure the resume goes through the same session (or one bound to the same thread) that received the suspension.
- Check for code paths that clear the current thread while suspensions are still pending.
Example fix
// before
await session.resumeSuspendedTool({ toolCallId, resumeData });
// after
if (!session.getCurrentThreadId?.()) {
await session.selectThread(originalThreadId);
}
await session.resumeSuspendedTool({ toolCallId, resumeData }); Defensive patterns
Strategy: validation
Validate before calling
// before resuming
const threadId = session.getCurrentThreadId?.();
if (!threadId) {
await session.selectThread(originalThreadId); // restore conversation context first
} Type guard
function canResume(session: { getCurrentThreadId?: () => string | undefined }): boolean {
return typeof session.getCurrentThreadId === 'function' && typeof session.getCurrentThreadId() === 'string';
} Try / catch
try {
await session.resumeSuspendedTool({ toolCallId, resumeData });
} catch (err) {
if (err instanceof Error && err.message.includes('without a current thread')) {
await session.selectThread(originalThreadId);
await session.resumeSuspendedTool({ toolCallId, resumeData });
} else throw err;
} Prevention
- Persist thread ID alongside suspension metadata so it can be restored after reloads.
- Resume via the same session/thread that received the suspension.
- Audit code paths that clear the current thread for dangling suspensions.
When it happens
Trigger: Calling resume on a valid suspension but on a session where `this.thread.getId()` is undefined — thread never set, thread cleared, or the suspension was handled by a different session instance than the one owning the conversation.
Common situations: Recreating a session (e.g., after a page reload) and resuming without restoring the thread; clearing thread state during resume; multi-session apps routing the resume to the wrong instance.
Related errors
- No active suspension to resume
- AGENT_RESUME_NO_SNAPSHOT_FOUND
- AGENT_RESUME_TOOL_CALL_NOT_SUSPENDED
- AGENT_SEND_STREAM_RESUME_NO_SUSPENDED_THREAD_RUN
- Received input message with wrong threadId. Input ${message.
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/0018d7edc4a709a9.
Report an issue: GitHub.