mastra-ai/mastra · error

Browser not launched

Error message

Browser not launched

What it means

requireStagehand resolves the Stagehand instance for the current thread, falling back to the shared manager. If neither a thread-scoped manager nor a shared manager exists, no browser was ever launched, so any Stagehand-dependent operation cannot proceed and the library throws. It is a guard ensuring you cannot interact with a browser that does not exist.

Source

Thrown at browser/stagehand/src/stagehand-browser.ts:458

      await this.threadManager.getManagerForThread(threadId);
      stagehand = this.threadManager.getExistingManagerForThread(threadId);
    }

    return stagehand ?? null;
  }

  /**
   * Require a Stagehand instance for the given or current thread.
   * Throws if no instance is available.
   * @param explicitThreadId - Optional thread ID to use instead of getCurrentThread()
   *                           Use this to avoid race conditions in concurrent tool calls.
   */
  private requireStagehand(explicitThreadId?: string): Stagehand {
    const threadId = explicitThreadId ?? this.getCurrentThread();
    const stagehand = this.threadManager.getExistingManagerForThread(threadId) ?? this.sharedManager;

    if (!stagehand) {
      throw new Error('Browser not launched');
    }
    return stagehand;
  }

  /**
   * Get the current page from Stagehand v3, respecting thread scope.
   * @param explicitThreadId - Optional thread ID to use instead of getCurrentThread()
   *                           Use this to avoid race conditions in concurrent tool calls.
   */
  private getPage(explicitThreadId?: string): V3Page | null {
    const scope = this.getScope();
    const threadId = explicitThreadId ?? this.getCurrentThread();

    // For 'thread' scope, get the thread's Stagehand's active page
    if (scope === 'thread' && threadId && threadId !== DEFAULT_THREAD_ID) {
      const stagehand = this.threadManager.getExistingManagerForThread(threadId);
      if (stagehand?.context) {
        return stagehand.context.activePage() as V3Page | null;

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Call launch()/connect() on the StagehandBrowser before using it
  2. Check that prior launch did not fail (it throws its own error) and that no early close() ran
  3. If using thread scope, operate from a thread that has an active session or rely on the shared manager
  4. Verify initialization order in your app lifecycle (e.g. launch in startup hook)

Example fix

// before
const page = await browser.stagehand().page;
// after
await browser.launch();
const page = await browser.stagehand().page;
Defensive patterns

Strategy: try-catch

Validate before calling

if (!browser.isBrowserRunning()) {
  await browser.launch();
}

Try / catch

try {
  const stagehand = browser.stagehand();
} catch (e) {
  if (e instanceof Error && e.message === 'Browser not launched') {
    await browser.launch();
  } else throw e;
}

Prevention

When it happens

Trigger: Calling the public stagehand accessor (or methods that route through requireStagehand, like getPage/actions) before calling launch/connect, or after the browser was closed such that no manager remains.

Common situations: Forgetting to await browser launch at startup; calling the browser from a different thread scope where only thread-scoped sessions exist; using a StagehandBrowser after close()/disconnect; race where launch failed silently upstream.

Related errors


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