{"record":{"id":"350fceedb3a2742a","repo":"elsa-workflows/elsa-core","slug":"a-configured-default-role-no-longer-exists","errorCode":null,"errorMessage":"A configured default role no longer exists.","messagePattern":"A configured default role no longer exists\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/modules/Elsa.ExternalAuthentication/Services/ExternalIdentityUserProvisioningService.cs","lineNumber":121,"sourceCode":"            ? await userStore.FindAsync(new()\n                { Id = user.Id }, cancellationToken) is not null\n            : await userProvider.FindAsync(new()\n                { Id = user.Id }, cancellationToken) is not null;\n\n    private static string NormalizeUserNamePrefix(string prefix)\n    {\n        var normalized = new string((prefix ?? string.Empty).Trim().Where(character => char.IsAsciiLetterOrDigit(character) || character is '-' or '_').ToArray());\n        return string.IsNullOrEmpty(normalized) ? \"external\" : normalized;\n    }\n\n    private async ValueTask<IReadOnlyCollection<string>> ResolveRoleIdsAsync(IReadOnlyCollection<string>? roleIds, CancellationToken cancellationToken)\n    {\n        var requested = (roleIds ?? []).Where(x => !string.IsNullOrWhiteSpace(x)).Distinct(StringComparer.Ordinal).ToArray();\n        if (requested.Length == 0)\n            return [];\n        var found = (await roleProvider.FindByIdsAsync(requested, cancellationToken)).Select(x => x.Id).ToHashSet(StringComparer.Ordinal);\n        if (!found.SetEquals(requested))\n            throw new InvalidOperationException(\"A configured default role no longer exists.\");\n        return requested;\n    }\n}\n","sourceCodeStart":103,"sourceCodeEnd":125,"githubUrl":"https://github.com/elsa-workflows/elsa-core/blob/fe9217bdfa0e27f0e09e45006eb6898f616e513d/src/modules/Elsa.ExternalAuthentication/Services/ExternalIdentityUserProvisioningService.cs#L103-L125","documentation":"ResolveRoleIdsAsync validates the proposal's DefaultRoleIds against the role provider: every requested, non-empty, distinct role ID must be found by IRoleProvider.FindByIdsAsync. If any ID is missing (SetEquals fails), ResolveAsync throws, because creating a user with dangling role references would produce a broken identity. Roles are typically seeded by configuration or modules, so this usually means configuration drift or a missing seed.","triggerScenarios":"ProvisioningRequest.Proposal.DefaultRoleIds contains a role ID that does not exist in the role store — e.g. roles configured by ID in appsettings that were never created, a role deleted by an admin, roles from another tenant/database not present in the current role provider, or IDs containing stale values after re-seeding.","commonSituations":"Configuring DefaultRoleIds with role names instead of role IDs; environment drift (staging config copied to production where roles have different IDs); roles deleted during cleanup; database recreated without running role seeders.","solutions":["Look up the actual role IDs in the Elsa role store and update the configured DefaultRoleIds to match exactly (IDs, not names).","Create the missing roles (via the admin API, studio, or role seeding) so the configured IDs resolve.","Verify the app connects to the database where those roles exist (tenant/environment mismatch).","Remove the stale role ID from DefaultRoleIds if the role is intentionally retired."],"exampleFix":"// before: appsettings\n\"DefaultRoleIds\": [\"Administrator\"] // name, not ID\n// after: use the real role ID from the Roles table\n\"DefaultRoleIds\": [\"role-7f3a2b9c\"],\n// or validate before provisioning:\nvar roles = await roleProvider.FindByIdsAsync(proposal.DefaultRoleIds, ct);\n// ensure roles.Count == proposal.DefaultRoleIds.Distinct().Count()","handlingStrategy":"validation","validationCode":"var requested = (proposal.DefaultRoleIds ?? []).Where(x => !string.IsNullOrWhiteSpace(x)).Distinct().ToList();\nvar found = (await roleProvider.FindByIdsAsync(requested, ct)).Select(r => r.Id).ToHashSet();\nif (!found.SetEquals(requested))\n{\n    var missing = requested.Except(found);\n    logger.LogError(\"Missing configured roles: {Missing}\", string.Join(\", \", missing));\n    proposal.DefaultRoleIds = requested.Where(found.Contains).ToList(); // or abort provisioning\n}","typeGuard":"async Task<bool> AllRolesExistAsync(IReadOnlyCollection<string> roleIds, CancellationToken ct)\n{\n    var requested = roleIds.Where(x => !string.IsNullOrWhiteSpace(x)).Distinct().ToList();\n    if (requested.Count == 0) return true;\n    var found = (await roleProvider.FindByIdsAsync(requested, ct)).Select(r => r.Id).ToHashSet();\n    return found.SetEquals(requested);\n}","tryCatchPattern":"try\n{\n    var (user, created) = await provisioning.ResolveAsync(request, ct: ct);\n}\ncatch (InvalidOperationException ex) when (ex.Message == \"A configured default role no longer exists.\")\n{\n    logger.LogError(ex, \"Configured DefaultRoleIds reference missing roles; check role seeding/config\");\n    throw; // do not create a user with missing roles\n}","preventionTips":["Store role IDs, not role names, in DefaultRoleIds configuration and verify them on startup.","Run role seeding on every environment and after database recreation.","Guard role deletion against references from provisioning configuration.","Log which specific role IDs are missing to speed up diagnosis."],"tags":["identity","roles","configuration","referential-integrity"],"backgroundTag":"entity-not-found","analyzedSha":"fe9217bdfa0e27f0e09e45006eb6898f616e513d","analyzedAt":"2026-09-13T20:32:34.702Z","contentChangedAt":"2026-09-13T20:32:34.702Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}