Skip to content

Android Permissions.RequestAsync can poison future permission requests after repeated off-main-thread failuresΒ #35861

Closed

Description

@AdamEssenmacher

Description

On Android, Permissions.RequestAsync<T>() appears to add a pending permission request to an internal static request table before validating that the call is running on the main thread. If the API is called from a background thread while the permission is not already granted, it throws the expected PermissionException, but the pending request entry is left behind because no Android permission request was launched and OnRequestPermissionsResult will never be called.

In normal app code, this is fairly unlikely to become user-visible. A single invalid off-main-thread call only leaks a small pending request entry for the lifetime of the process. The more serious failure mode requires repeated bad calls, such as a retry loop, startup/resume loop, or background helper that catches and swallows the exception.

However, once enough stale entries accumulate, MAUI’s Android permission request-code ring can wrap and collide with the stale entries. At that point, even a valid main-thread permission request can fail before Android is asked to show the permission prompt.

This is worth tracking as a known issue because runaway invalid permission-request code can put the app process into a poisoned state where subsequent MAUI permission requests fail until the app process is restarted.

Steps to Reproduce

Steps To Reproduce

  1. Create a new .NET MAUI Android app.

  2. Add a runtime permission to Platforms/Android/AndroidManifest.xml, for example:

<uses-permission android:name="android.permission.CAMERA" />
  1. Make sure the permission is not already granted for the app.
adb shell pm revoke <package-name> android.permission.CAMERA
  1. From app code, repeatedly call Permissions.RequestAsync<T>() from a background thread and swallow the expected exception:
for (var i = 0; i < 999; i++)
{
    try
    {
        await Task.Run(() => Permissions.RequestAsync<Permissions.Camera>());
    }
    catch (PermissionException)
    {
        // Expected: "Permission request must be invoked on main thread."
    }
}
  1. After the loop finishes, request the same permission normally from the main thread:
await Permissions.RequestAsync<Permissions.Camera>();

Expected Result

The main-thread permission request should either show the Android permission prompt or return the current permission status.

Actual Result

The main-thread request fails before Android is asked to show a permission prompt:

System.ArgumentException: An item with the same key has already been added. Key: 12001

The failed background-thread calls leave entries in MAUI’s static Android permission-request table. After enough failures, the request-code ring wraps and later valid permission requests collide with stale entries.

Link to public reproduction project repository

No response

Version with bug

10.0.70

Is this a regression from previous behavior?

Not sure, did not test other versions

Last version that worked well

Unknown/Other

Affected platforms

Android

Affected platform versions

No response

Did you find any workaround?

No response

Relevant log output

Activity

  1. nivetha-nagalingam commented on Jun 11, 2026

    @nivetha-nagalingam
    Contributor

    This issue has been validated in Visual Studio Code 1.124.0 with .NET MAUI versions 10.0.0 , 10.0.70 10.0.71. It can be reproducible on Android platform.
    PermissionIssue 1.zip

    Screen.Recording.2026-06-11.at.4.11.20.PM.mov
  2. added
    s/verifiedVerified / Reproducible Issue ready for Engineering Triage
    s/triagedIssue has been reviewed
    on Jun 11, 2026
  3. added this to the Backlog milestone on Jun 15, 2026
  4. jpd21122012 commented on Aug 3, 2026

    @jpd21122012
    Contributor

    I investigated this issue and was able to reproduce it on the current main branch (.NET MAUI 10.0.90).

    I have submitted a PR with a minimal fix that validates the main thread before registering the pending permission request, preventing stale entries from being added after off-main-thread failures.

    The PR also includes Android device tests covering this regression.

    Linked PR: #37056

  5. added a commit that references this issue on Aug 8, 2026
    7644597
  6. added a commit that references this issue on Aug 12, 2026
    77d0b8d
  7. 3 remaining items

  8. added 11 commits that reference this issue on Sep 2, 2026
    490f78c
    1f6545f
    66918d0
    40d542f
    eb350e3
    dd4497b
    fe3429f
    56011ec
    c6b0cf1
    d025017
    82ab74e
  9. locked and limited conversation to collaborators on Oct 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-essentialsEssentials: Device, Display, Connectivity, Secure Storage, Sensors, App Infopartner/syncfusionIssues / PR's with Syncfusion collaborationplatform/androids/triagedIssue has been revieweds/verifiedVerified / Reproducible Issue ready for Engineering Triaget/bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions