microsoft/playwright · error · Error

Connecting to Android devices is not allowed.

Error message

Connecting to Android devices is not allowed.

What it means

Thrown by AndroidDispatcher.devices() when _denyLaunch is true. The Playwright server sets denyLaunch in pre-launched / connect / launch-server modes (playwrightServer.ts, playwrightWebSocketServer.ts, playwrightPipeServer.ts): once the server has pinned or pre-launched a single Android device for the connection, enumerating further devices is forbidden to keep the session isolated.

Source

Thrown at packages/playwright-core/src/server/dispatchers/androidDispatcher.ts:38

import { AndroidDevice } from '../android/android';
import { SdkObject } from '../instrumentation';

import type { RootDispatcher } from './dispatcher';
import type { Android, SocketBackend } from '../android/android';
import type * as channels from '../channels';
import type { Progress } from '../progress';

export class AndroidDispatcher extends Dispatcher<Android, channels.AndroidChannel, RootDispatcher> implements channels.AndroidChannel {
  _type_Android = true;
  private readonly _denyLaunch: boolean;
  constructor(scope: RootDispatcher, android: Android, denyLaunch: boolean) {
    super(scope, android, 'Android', {});
    this._denyLaunch = denyLaunch;
  }

  async devices(params: channels.AndroidDevicesParams, progress: Progress): Promise<channels.AndroidDevicesResult> {
    if (this._denyLaunch)
      throw new Error(`Connecting to Android devices is not allowed.`);
    const devices = await this._object.devices(progress, params);
    return {
      devices: devices.map(d => AndroidDeviceDispatcher.from(this, d))
    };
  }
}

export class AndroidDeviceDispatcher extends Dispatcher<AndroidDevice, channels.AndroidDeviceChannel, AndroidDispatcher> implements channels.AndroidDeviceChannel {
  _type_AndroidDevice = true;

  static from(scope: AndroidDispatcher, device: AndroidDevice): AndroidDeviceDispatcher {
    const result = scope.connection.existingDispatcher<AndroidDeviceDispatcher>(device);
    return result || new AndroidDeviceDispatcher(scope, device);
  }

  constructor(scope: AndroidDispatcher, device: AndroidDevice) {
    super(scope, device, 'AndroidDevice', {
      model: device.model,

View on GitHub (pinned to c8fc3bf8d3)

Solutions

  1. Do not call devices() on a denyLaunch server; use the preLaunchedAndroidDevice already provided in the init result (the device the server pinned for you).
  2. If you truly need to enumerate devices, start the server in a mode without denyLaunch (plain local android.launchServer / direct driver) instead of preLaunchedAndroidDevice mode.
  3. Inspect the connection's init result (preLaunchedAndroidDevice / denyLaunch flag) once at startup and branch your code accordingly.

Example fix

// before: always enumerate
const [device] = await playwright._android.devices();

// after: use the server-provided device when present
const device = initResult.preLaunchedAndroidDevice ?? (await playwright._android.devices())[0];
if (!device) throw new Error('no device available');
Defensive patterns

Strategy: validation

Validate before calling

// Check the connection's init result before calling devices().
const init = await playwright.initialize();
if (init.denyLaunch || init.preLaunchedAndroidDevice) {
  // Use init.preLaunchedAndroidDevice instead of devices().
} else {
  const [device] = await playwright._android.devices();
}

Type guard

function hasPreLaunchedAndroidDevice(init: any): init is { preLaunchedAndroidDevice: AndroidDevice } {
  return !!init?.preLaunchedAndroidDevice;
}

Try / catch

try {
  await playwright._android.devices();
} catch (e) {
  if (/Android devices is not allowed/.test(e.message)) {
    // server denied enumeration; fall back to the preLaunchedAndroidDevice
  } else throw e;
}

Prevention

When it happens

Trigger: Calling playwright._android.devices() (or the Android.devices RPC) over a connection whose PlaywrightInitializeResult carried denyLaunch=true. This includes preLaunchedAndroidDevice mode (_initPreLaunchedAndroidMode) and any server started with a constrained launch policy. The check happens before any adb work, so it fires on every devices() call in such sessions.

Common situations: Connecting to a remote Playwright server that was started with a pre-paired Android device (e.g. for a managed device farm or CI grid) and then calling devices() as if it were a local session; using a launchServer-style endpoint for Android automation; misconfigured MCP/remote session that assumes open device enumeration.

Related errors


AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12). Data as JSON: /api/errors/ba8fd513c69f2cd3. Report an issue: GitHub.