mastra-ai/mastra · error

stagehand_close requires agent.threadId when browser scope i

Error message

stagehand_close requires agent.threadId when browser scope is not shared

What it means

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.

Source

Thrown at browser/stagehand/src/tools/close.ts:21

 */

import { createTool } from '@mastra/core/tools';
import { closeInputSchema } from '../schemas';
import type { StagehandBrowser } from '../stagehand-browser';
import { STAGEHAND_TOOLS } from './constants';

export function createCloseTool(browser: StagehandBrowser) {
  return createTool({
    id: STAGEHAND_TOOLS.CLOSE,
    description: 'Close the browser. Only use when done with all browsing.',
    inputSchema: closeInputSchema,
    execute: async (_input, { agent }) => {
      // For thread scope, close only the thread's session
      const threadId = agent?.threadId;
      browser.setCurrentThread(threadId);
      if (browser.getScope() !== 'shared') {
        if (!threadId) {
          throw new Error('stagehand_close requires agent.threadId when browser scope is not shared');
        }
        browser.markBrowserCloseReason('agent', threadId);
        await browser.closeThreadSession(threadId);
        return {
          success: true,
          hint: "Thread's browser session closed. A new session will be created on next use.",
        };
      }
      // For shared scope, close the entire browser
      browser.markBrowserCloseReason('agent');
      await browser.close();
      return {
        success: true,
        hint: 'Browser closed. It will be re-launched automatically on next use.',
      };
    },
  });
}

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Run the agent within a thread so agent.threadId is populated (enable memory / pass threadId in the run options)
  2. Switch browser scope to 'shared' if all agents should share one browser session, removing the threadId requirement
  3. Ensure the tool is invoked through the standard agent execution path (createStagehandTools wired into the agent) rather than called directly without an agent context

Example fix

// before
const tools = createStagehandTools({ scope: 'thread' });
await agent.generate(prompt); // no threadId -> error on stagehand_close
// after
await memory.createThread({ resourceId, threadId });
await agent.generate(prompt, { memory: { thread: threadId, resource: resourceId } });
Defensive patterns

Strategy: validation

Validate before calling

if (scope !== 'shared' && !agent?.threadId) {
  throw new Error('stagehand_close needs a thread: run the agent inside a thread or use scope "shared"');
}

Type guard

function hasThreadContext(agent: unknown): agent is { threadId: string } {
  return typeof agent === 'object' && agent !== null && 'threadId' in agent && typeof (agent as { threadId?: unknown }).threadId === 'string' && (agent as { threadId: string }).threadId.length > 0;
}

Try / catch

try {
  await stagehandCloseTool.execute({}, { agent });
} catch (e) {
  if ((e as Error).message.includes('requires agent.threadId')) {
    // fall back to shared scope or re-run within a thread context
  } else throw e;
}

Prevention

When it happens

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

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

Related errors


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