{"record":{"id":"0018d7edc4a709a9","repo":"mastra-ai/mastra","slug":"cannot-resume-a-suspended-tool-without-a-current-t","errorCode":null,"errorMessage":"Cannot resume a suspended tool without a current thread","messagePattern":"Cannot resume a suspended tool without a current thread","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/agent-controller/session.ts","lineNumber":3872,"sourceCode":"  }): Promise<void> {\n    const suspension = this.suspensions.get({ toolCallId });\n    if (!suspension) {\n      throw new Error('No active suspension to resume');\n    }\n\n    const agent = this.machinery.getAgent();\n\n    // Remove before resuming so a re-suspend during the resumed run can\n    // re-register the same toolCallId without being clobbered by this cleanup.\n    // Drop the matching display-state entry too so the UI stops rendering the\n    // resolved prompt while any other parked suspensions stay visible.\n    this.suspensions.delete({ toolCallId });\n    this.displayState.deletePendingSuspension(toolCallId);\n\n    const requestContext = await this.machinery.buildRequestContext(requestContextInput);\n    const threadId = this.thread.getId();\n    if (!threadId) {\n      throw new Error('Cannot resume a suspended tool without a current thread');\n    }\n\n    await this.thread.ensureSubscription(threadId);\n    const resumedSubscriptionBoundary = this.createSubscribedResumeBoundaryWaiter(\n      suspension.toolName === 'submit_plan' ? toolCallId : undefined,\n    );\n\n    try {\n      const resourceId = this.identity.getResourceId();\n      const sharedOptions = this.machinery.buildSharedRunOptions();\n      // Interactive builtins suspend to collect user input, not for approval.\n      // The resume data is the user's answer (a bare string), which the approval\n      // re-check would reject because it cannot carry an `{ approved }` field.\n      // Exempt these tools so the answer reaches the model as-is.\n      const isInteractive = suspension.toolName === 'ask_user' || suspension.toolName === 'request_access';\n      if (isInteractive) {\n        sharedOptions.requireToolApproval = false;\n      }","sourceCodeStart":3854,"sourceCodeEnd":3890,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/core/src/agent-controller/session.ts#L3854-L3890","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nawait session.resumeSuspendedTool({ toolCallId, resumeData });\n\n// after\nif (!session.getCurrentThreadId?.()) {\n  await session.selectThread(originalThreadId);\n}\nawait session.resumeSuspendedTool({ toolCallId, resumeData });","handlingStrategy":"validation","validationCode":"// before resuming\nconst threadId = session.getCurrentThreadId?.();\nif (!threadId) {\n  await session.selectThread(originalThreadId); // restore conversation context first\n}","typeGuard":"function canResume(session: { getCurrentThreadId?: () => string | undefined }): boolean {\n  return typeof session.getCurrentThreadId === 'function' && typeof session.getCurrentThreadId() === 'string';\n}","tryCatchPattern":"try {\n  await session.resumeSuspendedTool({ toolCallId, resumeData });\n} catch (err) {\n  if (err instanceof Error && err.message.includes('without a current thread')) {\n    await session.selectThread(originalThreadId);\n    await session.resumeSuspendedTool({ toolCallId, resumeData });\n  } else throw err;\n}","preventionTips":["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."],"tags":["agent","suspension","resume","thread"],"backgroundTag":"missing-thread-context","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T08:17:16.595Z"}