mastra-ai/mastra · error

CDP session not available for mouse injection

Error message

CDP session not available for mouse injection

What it means

injectMouseEvent dispatches mouse events over a CDP session bound to the thread's active page. If getCdpSessionForThread resolves no session (never launched, closed, or page detached), there is nowhere to send Input.dispatchMouseEvent, so the method throws.

Source

Thrown at browser/browser-viewer/src/browser-viewer.ts:372

        for (const [page, listener] of pageListeners) {
          page.off('framenavigated', listener);
        }
        pageListeners.clear();
      });
    }

    await stream.start();
    return stream;
  }

  // ---------------------------------------------------------------------------
  // Input Injection
  // ---------------------------------------------------------------------------

  override async injectMouseEvent(params: MouseEventParams, threadId?: string): Promise<void> {
    const cdpSession = await this.threadManager.getCdpSessionForThread(threadId ?? this.getCurrentThread());
    if (!cdpSession) {
      throw new Error('CDP session not available for mouse injection');
    }

    await cdpSession.send('Input.dispatchMouseEvent', params);
  }

  override async injectKeyboardEvent(params: KeyboardEventParams, threadId?: string): Promise<void> {
    const cdpSession = await this.threadManager.getCdpSessionForThread(threadId ?? this.getCurrentThread());
    if (!cdpSession) {
      throw new Error('CDP session not available for keyboard injection');
    }

    await cdpSession.send('Input.dispatchKeyEvent', params);
  }

  // ---------------------------------------------------------------------------
  // Tools (CLI agents don't use SDK tools - they use workspace commands)
  // ---------------------------------------------------------------------------

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Reconnect or relaunch the browser session for the target thread before injecting.
  2. Pass an explicit threadId with a live session instead of relying on defaults.
  3. Check session availability (or catch) and recreate the session, then retry.
  4. Treat browser_close as invalidating pending injections.

Example fix

// before
await viewer.injectMouseEvent(params, 'unknown-thread'); // throws
// after
if (!(await viewer.threadManager.getCdpSessionForThread(threadId))) {
  await viewer.reconnectThread(threadId);
}
await viewer.injectMouseEvent(params, threadId);
Defensive patterns

Strategy: validation

Validate before calling

const session = await viewer.threadManager.getCdpSessionForThread(threadId ?? viewer.getCurrentThread());
if (!session) {
  await viewer.reconnectThread(threadId); // or show a 'reconnecting' UI state
} else {
  await viewer.injectMouseEvent(params, threadId);
}

Type guard

type CdpSession = NonNullable<Awaited<ReturnType<typeof viewer.threadManager.getCdpSessionForThread>>>;
function isCdpSessionAvailable(s: CdpSession | null | undefined): s is CdpSession {
  return s != null;
}

Try / catch

try {
  await viewer.injectMouseEvent(params, threadId);
} catch (err) {
  if (err instanceof Error && err.message.includes('not available for mouse injection')) {
    await viewer.reconnectThread(threadId);
    await viewer.injectMouseEvent(params, threadId);
    return;
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling injectMouseEvent when the thread has no running browser session, after the session/page was closed, or with a threadId that never launched a browser; also when the default current thread has no session.

Common situations: Remote viewer clicking a page after the agent session timed out; passing undefined threadId while the current thread lacks a session; CDP detachment after tab close or crash.

Related errors


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