Skip to content

Unable to re-use AppRoleAssignment instance #3197

Open

Description

@ssteiner

Describe the bug

I'm creating App Registrations, Service Principals, then assign an App role to that service principal.

The App role assignment is done like this:

var assignment = new AppRoleAssignment { PrincipalId = Guid.Parse(servicePrincipalObjectId), ResourceId = Guid.Parse(api.Id!), AppRoleId = role.Id };

await graph.ServicePrincipals[servicePrincipalId].AppRoleAssignments.PostAsync(assignment);`

Depending on timing it could be that this operation returns an ODataError with code 'Request_ResourceNotFound' (I really wished Microsoft would learn the concept of hot writes... feels kinda amateur hour to have retries everywhere, but I digress). So, I created a retry mechanism when getting that exception that just calls AppRoleAssignments.PostAsync again after some time with circuit breaker and all. However, from the second invocation of AppRoleAssignments.PostAsync fails with an 'AdditionalData can not be null' error.

So it seems the aborted operation changes the AppRoleAssignment in a way that it can't be re-used and has to be constructed again.

Expected behavior

I would expect to be able to re-use my AppRoleAssignment object

How to reproduce

You need to create an App Registration with a custom role to run this sample. Run under an account that has AppRoleAssignment.ReadWrite.All and Application.ReadWrite.OwnedBy permissions.

All error handling and retry behavior has been stripped away for brevity

    var app = await graph.Applications.PostAsync(
        new Application { DisplayName = displayName, SignInAudience = "AzureADMyOrg" });

    var sp = await graph.ServicePrincipals.PostAsync(
        new ServicePrincipal { AppId = app.Id });

    var apiAppId = "ClientId of App Registration that has the 'name of-custom-role' role";

    var apis = await graph.ServicePrincipals.GetAsync(r =>
    {
        r.QueryParameters.Filter = $"appId eq '{apiAppId}'";
    }).ConfigureAwait(false);

    var roleValue = "name-of-custom-role-on";

    var api = apis?.Value?.FirstOrDefault();

    var role = api.AppRoles?.FirstOrDefault(x => x.Value == roleValue);

    var assignment = new AppRoleAssignment
    {
        PrincipalId = Guid.Parse(sp.Id),
        ResourceId = Guid.Parse(api.Id!),
        AppRoleId = role.Id
    };

    await graph.ServicePrincipals[servicePrincipalId].AppRoleAssignments.PostAsync(assignment);

SDK Version

5.51.0

Latest version known to work for scenario above?

No response

Known Workarounds

re-instantiate AppRoleAssignment before trying the create the app role assignment again.

Debug output

Click to expand log ```
</details>


### Configuration

- OS: Win 11 2025H2
- Architecture: x64
- Issue is present when deployed on Azure App service as well so it's not environment specific
- the SDK is included with the package Microsoft.Identity.Web.GraphServiceClient 4.16.1.

### Other information

_No response_

Activity

  1. Saad-elnagar commented on Oct 21, 2026

    @Saad-elnagar

    Hi! I’d like to investigate this issue and contribute a fix.

    The behavior described here is worth addressing: when a PostAsync request fails, retrying with the same AppRoleAssignment instance results in an AdditionalData can not be null error, forcing callers to recreate the model just to retry the operation.

    I’d like to trace how the request serialization pipeline handles the model instance, identify what changes during the first request, and determine whether the SDK can safely support reusing the same request model across retries.

    My proposed approach is to:

    • Reproduce the issue with a focused regression test.
    • Investigate whether the request pipeline mutates or otherwise changes the model during serialization.
    • Implement the smallest fix that preserves the expected retry behavior.
    • Add tests covering both the initial request and a subsequent retry using the same instance.

    Would it be okay for me to investigate this and submit a PR with the fix and regression tests?

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

    status:waiting-for-triageAn issue that is yet to be reviewed or assignedtype:bugA broken experience

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions