stablyai/orca · error · EmulatorError

emulator_error

emulator_error

Error message

pm ${op} requires a permission name

What it means

Thrown by permissionArgs when op is 'grant' or 'revoke' but the permission argument is undefined, null, or empty/whitespace. The builder cannot construct a valid `adb shell pm grant <pkg> <perm>` line without a permission target. The 'reset' op is exempt because `pm reset-permissions` takes no permission argument.

Source

Thrown at src/main/emulator/android/android-permissions.ts:25

// grant/revoke: `adb -s <serial> shell pm <op> <package> <permission>`
// reset:        `adb -s <serial> shell pm reset-permissions <package>`
// `reset` clears every runtime grant, so the permission argument is ignored.
export function permissionArgs(
  serial: string,
  op: AndroidPermissionOp,
  packageName: string,
  permission?: string
): string[] {
  const base = ['-s', serial, 'shell', 'pm']
  // `pm reset-permissions` resets every app's runtime grants and rejects a
  // package argument, so it is omitted here.
  if (op === 'reset') {
    return [...base, 'reset-permissions']
  }
  // grant/revoke target a single permission, so it must be present.
  if (!permission || permission.trim() === '') {
    throw new EmulatorError('emulator_error', `pm ${op} requires a permission name`)
  }
  return [...base, op, packageName, permission]
}

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Validate the permission name is a non-empty Android permission constant (e.g. android.permission.CAMERA) before calling permissionArgs.
  2. Filter empty entries out of permission lists before iterating.
  3. Skip grant/revoke entirely when no permission applies; use op 'reset' only when a full reset is intended.

Example fix

// before: empty permission from config slips through
for (const p of manifest.permissions) {
  await run(permissionArgs(serial, 'grant', pkg, p))
}
// after: guard non-empty canonical names
for (const p of manifest.permissions) {
  if (!p?.startsWith('android.permission.')) continue
  await run(permissionArgs(serial, 'grant', pkg, p))
}
Defensive patterns

Strategy: validation

Validate before calling

// Guard the permission argument before building args.
function isValidPermission(p?: string): boolean {
  return !!p && p.trim() !== '' && p.startsWith('android.permission.')
}

Type guard

function isPermissionName(p?: string): p is string {
  return !!p && p.trim() !== ''
}

Prevention

When it happens

Trigger: permissionArgs(serial, 'grant', pkg) or permissionArgs(serial, 'revoke', pkg) invoked with the fourth argument omitted or set to ''/whitespace. Typically a caller reading the permission name from a config or list that yielded an empty value.

Common situations: A permission grant loop iterating over a list that contains an empty entry; a config field left blank; reading the permission name from a JSON manifest that did not include the expected key; a guard missing before forwarding user input.

Related errors


AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12). Data as JSON: /api/errors/a5b16cb1a46cbb41. Report an issue: GitHub.