termux/termux-app · error · IllegalStateException

%1$s requires "Display over other apps" permission to start

Error message

%1$s requires "Display over other apps" permission to start activities and services from background on Android >= 10. Grants it from Android Settings -> Apps -> %1$s -> Advanced -> Draw over other apps. The permission name may be different on different devices, like on Xiaomi, its called "Display pop-up windows while running in the background", check https://dontkillmyapp.com for device specific issues.

What it means

Thrown by AmSocketServer.runAmCommand when, on Android 10+, a 'start' or 'startservice' am subcommand is requested but the app lacks the SYSTEM_ALERT_WINDOW ('Display over other apps') permission. Starting activities/services from the background requires this permission on API 29+. The message is a localized resource string formatted with the app name.

Source

Thrown at termux-shared/src/main/java/com/termux/shared/shell/am/AmSocketServer.java:222

     * @param stderr The {@link StringBuilder} to set stderr in that is returned by the am command.
     * @param checkDisplayOverAppsPermission Check if {@link Manifest.permission#SYSTEM_ALERT_WINDOW}
     *                                       has been granted if running on Android `>= 10` and
     *                                       starting activity or service.
     * @return Returns the {@code error} if am command failed, otherwise {@code null}.
     */
    public static Error runAmCommand(@NonNull Context context,
                                     String[] amCommandArray,
                                     @NonNull StringBuilder stdout, @NonNull StringBuilder stderr,
                                     boolean checkDisplayOverAppsPermission) {
        try (ByteArrayOutputStream stdoutByteStream = new ByteArrayOutputStream();
             PrintStream stdoutPrintStream = new PrintStream(stdoutByteStream);
             ByteArrayOutputStream stderrByteStream = new ByteArrayOutputStream();
             PrintStream stderrPrintStream = new PrintStream(stderrByteStream)) {

            if (checkDisplayOverAppsPermission && amCommandArray.length >= 1 &&
                (amCommandArray[0].equals("start") || amCommandArray[0].equals("startservice")) &&
                !PermissionUtils.validateDisplayOverOtherAppsPermissionForPostAndroid10(context, true)) {
                throw new IllegalStateException(context.getString(R.string.error_display_over_other_apps_permission_not_granted,
                    PackageUtils.getAppNameForPackage(context)));
            }

            new Am(stdoutPrintStream, stderrPrintStream, (Application) context.getApplicationContext()).run(amCommandArray);

            // Set stdout to value set by am command in stdoutPrintStream
            stdoutPrintStream.flush();
            stdout.append(stdoutByteStream.toString(StandardCharsets.UTF_8.name()));

            // Set stderr to value set by am command in stderrPrintStream
            stderrPrintStream.flush();
            stderr.append(stderrByteStream.toString(StandardCharsets.UTF_8.name()));
        } catch (Exception e) {
            return AmSocketServerErrno.ERRNO_RUN_AM_COMMAND_FAILED_WITH_EXCEPTION.getError(e, Arrays.toString(amCommandArray), e.getMessage());
        }

        return null;
    }

View on GitHub (pinned to 3df69d1da1)

Solutions

  1. Request the user to grant 'Display over other apps' via Settings (the message links to it).
  2. Programmatically send the user to ACTION_MANAGE_OVERLAY_PERMISSION settings before issuing background start commands.
  3. Pass checkDisplayOverAppsPermission=false only if you have verified another valid way to start from background (rare).
  4. Check PermissionUtils.validateDisplayOverOtherAppsPermissionForPostAndroid10(context, false) before calling runAmCommand and degrade gracefully.

Example fix

// before
if (checkDisplayOverAppsPermission && ... &&
    !PermissionUtils.validateDisplayOverOtherAppsPermissionForPostAndroid10(context, true)) {
    throw new IllegalStateException(context.getString(R.string.error_display_over_other_apps_permission_not_granted, ...));
}

// after (pre-check + send user to settings instead of throwing blindly)
if (checkDisplayOverAppsPermission && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
        && !Settings.canDrawOverOtherApps(context)) {
    Intent i = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + context.getPackageName()));
    i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    context.startActivity(i);
    return Error.withErrno(...); // signal caller to retry after grant
}
Defensive patterns

Strategy: validation

Validate before calling

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
        && ("start".equals(amCommandArray[0]) || "startservice".equals(amCommandArray[0]))
        && !Settings.canDrawOverOtherApps(context)) {
    // send user to settings instead of letting runAmCommand throw
    Intent i = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
        Uri.parse("package:" + context.getPackageName()));
    i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    context.startActivity(i);
    return;
}

Try / catch

try {
    AmSocketServer.runAmCommand(context, cmd, stdout, stderr, true);
} catch (IllegalStateException e) {
    if (e.getMessage() != null && e.getMessage().contains("Display over other apps")) {
        promptUserForOverlayPermission();
    } else throw e;
}

Prevention

When it happens

Trigger: Calling runAmCommand with an amCommandArray whose first element is 'start' or 'startservice', checkDisplayOverAppsPermission=true, on Android >= 10, while Settings.canDrawOverOtherApps() returns false.

Common situations: Fresh install without granting the overlay permission; background service tries to launch an activity; OEM battery-optimizer/background-killer reset the permission; plugin app invoking am from background.

Related errors


AI-assisted analysis of termux/termux-app@3df69d1da1 (2026-08-13). Data as JSON: /api/errors/2965378871135c6e. Report an issue: GitHub.