{"record":{"id":"ba8fd513c69f2cd3","repo":"microsoft/playwright","slug":"connecting-to-android-devices-is-not-allowed","errorCode":null,"errorMessage":"Connecting to Android devices is not allowed.","messagePattern":"Connecting to Android devices is not allowed\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/playwright-core/src/server/dispatchers/androidDispatcher.ts","lineNumber":38,"sourceCode":"import { AndroidDevice } from '../android/android';\nimport { SdkObject } from '../instrumentation';\n\nimport type { RootDispatcher } from './dispatcher';\nimport type { Android, SocketBackend } from '../android/android';\nimport type * as channels from '../channels';\nimport type { Progress } from '../progress';\n\nexport class AndroidDispatcher extends Dispatcher<Android, channels.AndroidChannel, RootDispatcher> implements channels.AndroidChannel {\n  _type_Android = true;\n  private readonly _denyLaunch: boolean;\n  constructor(scope: RootDispatcher, android: Android, denyLaunch: boolean) {\n    super(scope, android, 'Android', {});\n    this._denyLaunch = denyLaunch;\n  }\n\n  async devices(params: channels.AndroidDevicesParams, progress: Progress): Promise<channels.AndroidDevicesResult> {\n    if (this._denyLaunch)\n      throw new Error(`Connecting to Android devices is not allowed.`);\n    const devices = await this._object.devices(progress, params);\n    return {\n      devices: devices.map(d => AndroidDeviceDispatcher.from(this, d))\n    };\n  }\n}\n\nexport class AndroidDeviceDispatcher extends Dispatcher<AndroidDevice, channels.AndroidDeviceChannel, AndroidDispatcher> implements channels.AndroidDeviceChannel {\n  _type_AndroidDevice = true;\n\n  static from(scope: AndroidDispatcher, device: AndroidDevice): AndroidDeviceDispatcher {\n    const result = scope.connection.existingDispatcher<AndroidDeviceDispatcher>(device);\n    return result || new AndroidDeviceDispatcher(scope, device);\n  }\n\n  constructor(scope: AndroidDispatcher, device: AndroidDevice) {\n    super(scope, device, 'AndroidDevice', {\n      model: device.model,","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/microsoft/playwright/blob/c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6/packages/playwright-core/src/server/dispatchers/androidDispatcher.ts#L20-L56","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Do not call devices() on a denyLaunch server; use the preLaunchedAndroidDevice already provided in the init result (the device the server pinned for you).","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.","Inspect the connection's init result (preLaunchedAndroidDevice / denyLaunch flag) once at startup and branch your code accordingly."],"exampleFix":"// before: always enumerate\nconst [device] = await playwright._android.devices();\n\n// after: use the server-provided device when present\nconst device = initResult.preLaunchedAndroidDevice ?? (await playwright._android.devices())[0];\nif (!device) throw new Error('no device available');","handlingStrategy":"validation","validationCode":"// Check the connection's init result before calling devices().\nconst init = await playwright.initialize();\nif (init.denyLaunch || init.preLaunchedAndroidDevice) {\n  // Use init.preLaunchedAndroidDevice instead of devices().\n} else {\n  const [device] = await playwright._android.devices();\n}","typeGuard":"function hasPreLaunchedAndroidDevice(init: any): init is { preLaunchedAndroidDevice: AndroidDevice } {\n  return !!init?.preLaunchedAndroidDevice;\n}","tryCatchPattern":"try {\n  await playwright._android.devices();\n} catch (e) {\n  if (/Android devices is not allowed/.test(e.message)) {\n    // server denied enumeration; fall back to the preLaunchedAndroidDevice\n  } else throw e;\n}","preventionTips":["Inspect the init result (denyLaunch / preLaunchedAndroidDevice) once at connection time and cache the decision.","On managed servers, always use the pinned device rather than enumerating.","Document whether your server endpoint runs in preLaunchedAndroidDevice mode so clients know not to call devices()."],"tags":["android","deny-launch","server","remote","protocol"],"backgroundTag":null,"analyzedSha":"c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6","analyzedAt":"2026-08-12T07:26:36.950Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}