mastra-ai/mastra · error

CDP session not available for keyboard injection

Error message

CDP session not available for keyboard injection

What it means

injectKeyboardEvent sends Input.dispatchKeyEvent over a CDP session attached to the thread's page. When no CDP session exists for the given (or current) thread, the event has no target, so the method throws instead of failing silently.

Source

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

  }

  // ---------------------------------------------------------------------------
  // 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)
  // ---------------------------------------------------------------------------

  getTools(): Record<string, Tool> {
    // CLI agents use workspace_execute_command with CLI skills
    // No SDK tools needed
    return {};
  }
}

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Ensure a live browser session exists for the thread; reconnect before injecting.
  2. Pass an explicit threadId with a live session rather than relying on the current thread.
  3. Catch the error, re-establish the CDP session, and retry the keystroke.
  4. Verify no concurrent close/dispose is racing the injection.

Example fix

// before
await viewer.injectKeyboardEvent({ type: 'keyDown', key: 'Enter' }); // throws if no session
// after
if (!(await viewer.threadManager.getCdpSessionForThread(threadId))) {
  await viewer.reconnectThread(threadId);
}
await viewer.injectKeyboardEvent({ type: 'keyDown', key: 'Enter' }, threadId);
Defensive patterns

Strategy: try-catch

Validate before calling

const session = await viewer.threadManager.getCdpSessionForThread(threadId ?? viewer.getCurrentThread());
if (!session) throw new Error('Reconnect thread before keyboard injection');
await viewer.injectKeyboardEvent(params, threadId);

Type guard

function canInjectKeyboard(s: unknown): boolean {
  return s !== null && s !== undefined && typeof (s as { send?: unknown }).send === 'function';
}

Try / catch

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

Prevention

When it happens

Trigger: Calling injectKeyboardEvent when the thread's browser session does not exist, was closed, or the CDP session detached; same conditions as mouse injection but for keyboard events.

Common situations: Typing in the viewer after the agent session expired; injecting into a default current thread with no session; concurrent browser_close invalidating the session mid-interaction.

Related errors


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