expo/expo · error · CommandError

EXPO_ADB_USER

EXPO_ADB_USER

Error message

Invalid ADB user number "${userNumber}" set with environment variable EXPO_ADB_USER. Run "adb shell pm list users" to see valid user numbers.

What it means

Thrown by ADBServer.resolveAdbPromise (code EXPO_ADB_USER) when an adb command fails with exit status 255 and its stdout contains 'Bad user number'. This indicates the Android multi-user number configured via the EXPO_ADB_USER environment variable is invalid for the connected device/emulator. The error extracts the offending number from adb's output (or falls back to env.EXPO_ADB_USER) and points the user to list valid users.

Source

Thrown at packages/@expo/cli/src/start/platforms/android/ADBServer.ts:112

        stdio: 'pipe',
      })
    );
    event('adb_file_output', { output: results });
    return results;
  }

  /** Formats error info. */
  async resolveAdbPromise<T>(promise: T | Promise<T>): Promise<T> {
    try {
      return await promise;
    } catch (error: any) {
      // User pressed ctrl+c to cancel the process...
      if (error.signal === 'SIGINT') {
        throw new AbortCommandError();
      }
      if (error.status === 255 && error.stdout.includes('Bad user number')) {
        const userNumber = error.stdout.match(/Bad user number: (.+)/)?.[1] ?? env.EXPO_ADB_USER;
        throw new CommandError(
          'EXPO_ADB_USER',
          `Invalid ADB user number "${userNumber}" set with environment variable EXPO_ADB_USER. Run "adb shell pm list users" to see valid user numbers.`
        );
      }
      // TODO: Support heap corruption for adb 29 (process exits with code -1073740940) (windows and linux)
      let errorMessage = (error.stderr || error.stdout || error.message).trim();
      if (errorMessage.startsWith(BEGINNING_OF_ADB_ERROR_MESSAGE)) {
        errorMessage = errorMessage.substring(BEGINNING_OF_ADB_ERROR_MESSAGE.length);
      }

      error.message = errorMessage;
      throw error;
    }
  }
}

View on GitHub (pinned to b09195aac2)

Solutions

  1. List valid users: `adb shell pm list users` and note a valid user id (usually 0).
  2. Set EXPO_ADB_USER to a valid id, e.g. `export EXPO_ADB_USER=0`, or unset it to use the default.
  3. Remove the EXPO_ADB_USER line from your shell profile/.env if you don't need multi-user targeting.
  4. Reconnect the device and recheck users, since user ids can change after resets.

Example fix

// before: export EXPO_ADB_USER=10   # invalid on this device
// after:  export EXPO_ADB_USER=0
Defensive patterns

Strategy: validation

Validate before calling

import { env } from '../../utils/env';
import { executeAdbAsync } from './adb';

async function isValidAdbUser(user: string | undefined): Promise<boolean> {
  if (!user) return true;
  const out = await executeAdbAsync(['shell', 'pm', 'list', 'users']);
  return new RegExp(`UserInfo\\{${user}:`).test(out);
}

if (!(await isValidAdbUser(env.EXPO_ADB_USER))) {
  throw new Error(`EXPO_ADB_USER=${env.EXPO_ADB_USER} is not a valid user id on this device.`);
}

Try / catch

try {
  await adbServer.resolveAdbPromise(someAdbCall);
} catch (e: any) {
  if (e.code === 'EXPO_ADB_USER') {
    // unset EXPO_ADB_USER or set it to 0, then retry
  } else throw e;
}

Prevention

When it happens

Trigger: An adb invocation run through resolveAdbPromise rejects with error.status === 255 and error.stdout includes 'Bad user number', which happens when EXPO_ADB_USER points to a user id that doesn't exist on the device (e.g. using --user 999 on a single-user emulator).

Common situations: EXPO_ADB_USER set globally to a value valid on one device but not another. Copying an env var from a multi-user Android device to a single-user emulator. A stale .env or shell profile with an old user number. A device whose user list changed after a factory reset.

Related errors


AI-assisted analysis of expo/expo@b09195aac2 (2026-08-12). Data as JSON: /api/errors/3fa49abea9dee4b9. Report an issue: GitHub.