elsa-workflows/elsa-core · error · InvalidOperationException

A configured default role no longer exists.

Error message

A configured default role no longer exists.

What it means

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.

Solutions

  1. Look up the actual role IDs in the Elsa role store and update the configured DefaultRoleIds to match exactly (IDs, not names).
  2. Create the missing roles (via the admin API, studio, or role seeding) so the configured IDs resolve.
  3. Verify the app connects to the database where those roles exist (tenant/environment mismatch).
  4. Remove the stale role ID from DefaultRoleIds if the role is intentionally retired.

Example fix

// before: appsettings
"DefaultRoleIds": ["Administrator"] // name, not ID
// after: use the real role ID from the Roles table
"DefaultRoleIds": ["role-7f3a2b9c"],
// or validate before provisioning:
var roles = await roleProvider.FindByIdsAsync(proposal.DefaultRoleIds, ct);
// ensure roles.Count == proposal.DefaultRoleIds.Distinct().Count()
Defensive patterns

Strategy: validation

Validate before calling

var requested = (proposal.DefaultRoleIds ?? []).Where(x => !string.IsNullOrWhiteSpace(x)).Distinct().ToList();
var found = (await roleProvider.FindByIdsAsync(requested, ct)).Select(r => r.Id).ToHashSet();
if (!found.SetEquals(requested))
{
    var missing = requested.Except(found);
    logger.LogError("Missing configured roles: {Missing}", string.Join(", ", missing));
    proposal.DefaultRoleIds = requested.Where(found.Contains).ToList(); // or abort provisioning
}

Type guard

async Task<bool> AllRolesExistAsync(IReadOnlyCollection<string> roleIds, CancellationToken ct)
{
    var requested = roleIds.Where(x => !string.IsNullOrWhiteSpace(x)).Distinct().ToList();
    if (requested.Count == 0) return true;
    var found = (await roleProvider.FindByIdsAsync(requested, ct)).Select(r => r.Id).ToHashSet();
    return found.SetEquals(requested);
}

Try / catch

try
{
    var (user, created) = await provisioning.ResolveAsync(request, ct: ct);
}
catch (InvalidOperationException ex) when (ex.Message == "A configured default role no longer exists.")
{
    logger.LogError(ex, "Configured DefaultRoleIds reference missing roles; check role seeding/config");
    throw; // do not create a user with missing roles
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13). Data as JSON: /api/errors/350fceedb3a2742a. Report an issue: GitHub.

Appendix: source

Thrown at src/modules/Elsa.ExternalAuthentication/Services/ExternalIdentityUserProvisioningService.cs:121

            ? await userStore.FindAsync(new()
                { Id = user.Id }, cancellationToken) is not null
            : await userProvider.FindAsync(new()
                { Id = user.Id }, cancellationToken) is not null;

    private static string NormalizeUserNamePrefix(string prefix)
    {
        var normalized = new string((prefix ?? string.Empty).Trim().Where(character => char.IsAsciiLetterOrDigit(character) || character is '-' or '_').ToArray());
        return string.IsNullOrEmpty(normalized) ? "external" : normalized;
    }

    private async ValueTask<IReadOnlyCollection<string>> ResolveRoleIdsAsync(IReadOnlyCollection<string>? roleIds, CancellationToken cancellationToken)
    {
        var requested = (roleIds ?? []).Where(x => !string.IsNullOrWhiteSpace(x)).Distinct(StringComparer.Ordinal).ToArray();
        if (requested.Length == 0)
            return [];
        var found = (await roleProvider.FindByIdsAsync(requested, cancellationToken)).Select(x => x.Id).ToHashSet(StringComparer.Ordinal);
        if (!found.SetEquals(requested))
            throw new InvalidOperationException("A configured default role no longer exists.");
        return requested;
    }
}

View on GitHub (pinned to fe9217bdfa)