{"record":{"id":"7680aeb54048a48a","repo":"mastra-ai/mastra","slug":"stagehand-close-requires-agent-threadid-when-brows","errorCode":null,"errorMessage":"stagehand_close requires agent.threadId when browser scope is not shared","messagePattern":"stagehand_close requires agent\\.threadId when browser scope is not shared","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"browser/stagehand/src/tools/close.ts","lineNumber":21,"sourceCode":" */\n\nimport { createTool } from '@mastra/core/tools';\nimport { closeInputSchema } from '../schemas';\nimport type { StagehandBrowser } from '../stagehand-browser';\nimport { STAGEHAND_TOOLS } from './constants';\n\nexport function createCloseTool(browser: StagehandBrowser) {\n  return createTool({\n    id: STAGEHAND_TOOLS.CLOSE,\n    description: 'Close the browser. Only use when done with all browsing.',\n    inputSchema: closeInputSchema,\n    execute: async (_input, { agent }) => {\n      // For thread scope, close only the thread's session\n      const threadId = agent?.threadId;\n      browser.setCurrentThread(threadId);\n      if (browser.getScope() !== 'shared') {\n        if (!threadId) {\n          throw new Error('stagehand_close requires agent.threadId when browser scope is not shared');\n        }\n        browser.markBrowserCloseReason('agent', threadId);\n        await browser.closeThreadSession(threadId);\n        return {\n          success: true,\n          hint: \"Thread's browser session closed. A new session will be created on next use.\",\n        };\n      }\n      // For shared scope, close the entire browser\n      browser.markBrowserCloseReason('agent');\n      await browser.close();\n      return {\n        success: true,\n        hint: 'Browser closed. It will be re-launched automatically on next use.',\n      };\n    },\n  });\n}","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/browser/stagehand/src/tools/close.ts#L3-L39","documentation":"stagehand_close closes browser sessions for the current thread. When the browser scope is not 'shared' (i.e. per-thread sessions), the tool needs to know which thread's session to close, and derives it from agent.threadId. If threadId is missing the tool cannot identify a session to close, so it throws rather than silently closing the wrong session.","triggerScenarios":"Calling the stagehand_close tool (via createCloseTool, exported by createStagehandTools) with browser scope set to 'thread' or another non-shared scope while the executing agent has no threadId (e.g. run outside a thread/memory context, or tool invoked without an agent context).","commonSituations":"Running a Stagehand-enabled agent with memory disabled or threadless execution; invoking tools via a custom runner that does not propagate the thread context; misconfigured scope ('thread') combined with a harness that drops agent metadata.","solutions":["Run the agent within a thread so agent.threadId is populated (enable memory / pass threadId in the run options)","Switch browser scope to 'shared' if all agents should share one browser session, removing the threadId requirement","Ensure the tool is invoked through the standard agent execution path (createStagehandTools wired into the agent) rather than called directly without an agent context"],"exampleFix":"// before\nconst tools = createStagehandTools({ scope: 'thread' });\nawait agent.generate(prompt); // no threadId -> error on stagehand_close\n// after\nawait memory.createThread({ resourceId, threadId });\nawait agent.generate(prompt, { memory: { thread: threadId, resource: resourceId } });","handlingStrategy":"validation","validationCode":"if (scope !== 'shared' && !agent?.threadId) {\n  throw new Error('stagehand_close needs a thread: run the agent inside a thread or use scope \"shared\"');\n}","typeGuard":"function hasThreadContext(agent: unknown): agent is { threadId: string } {\n  return typeof agent === 'object' && agent !== null && 'threadId' in agent && typeof (agent as { threadId?: unknown }).threadId === 'string' && (agent as { threadId: string }).threadId.length > 0;\n}","tryCatchPattern":"try {\n  await stagehandCloseTool.execute({}, { agent });\n} catch (e) {\n  if ((e as Error).message.includes('requires agent.threadId')) {\n    // fall back to shared scope or re-run within a thread context\n  } else throw e;\n}","preventionTips":["Always run thread-scoped agents with memory/thread context enabled","Assert agent.threadId exists before invoking thread-scoped tools","Prefer 'shared' scope if your harness is threadless"],"tags":["browser","tooling","misconfiguration"],"backgroundTag":"missing-thread-context","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}