elsa-workflows/elsa-core · error · InvalidOperationException
A user creation proposal is required for an unlinked…
Error message
A user creation proposal is required for an unlinked external identity.
What it means
ResolveAsync throws this when request.ExistingUserId is empty (unlinked external identity, meaning a new user should be created) but request.Proposal is null. The proposal carries the default role IDs and user-name prefix needed to create the new credential-less Elsa user, so without it the service cannot proceed.
Solutions
- Supply a valid ProvisioningRequest.Proposal with DefaultRoleIds and UserNamePrefix when ExistingUserId is not set.
- If the identity should link to an existing user, set ExistingUserId instead of leaving both fields empty.
- Check the external-authentication configuration (default roles, user name prefix) so the provisioning pipeline can build a proposal.
Example fix
// before
var request = new ProvisioningRequest { TenantId = tenantId }; // no Proposal
// after
var request = new ProvisioningRequest
{
TenantId = tenantId,
Proposal = new UserCreationProposal { DefaultRoleIds = ["admin"], UserNamePrefix = "oidc" }
}; Defensive patterns
Strategy: validation
Validate before calling
if (string.IsNullOrWhiteSpace(request.ExistingUserId) && request.Proposal is null)
throw new ArgumentException("Either ExistingUserId or Proposal must be provided.", nameof(request)); Type guard
bool IsProvisionable(ProvisioningRequest r) => !string.IsNullOrWhiteSpace(r.ExistingUserId) || r.Proposal is not null;
Try / catch
try
{
var (user, created) = await provisioning.ResolveAsync(request, ct: ct);
}
catch (InvalidOperationException ex) when (ex.Message == "A user creation proposal is required for an unlinked external identity.")
{
request.Proposal = proposalFromConfiguration; // load defaults from options
var (user, created) = await provisioning.ResolveAsync(request, ct: ct);
} Prevention
- Build ProvisioningRequest through a factory that enforces the ExistingUserId-or-Proposal invariant.
- Bind default proposal values (roles, prefix) from configuration so they are never null.
- Add a unit test asserting IsProvisionable for all request construction paths.
When it happens
Trigger: Calling ResolveAsync with a ProvisioningRequest that has neither ExistingUserId nor Proposal set — e.g. constructing the request manually and forgetting to supply the proposal, or an external identity provider flow that never configured default user creation options.
Common situations: Custom login integrations that build ProvisioningRequest by hand; upgrading external authentication packages where a previously optional proposal field was not populated; configuration sections for default roles/prefix omitted so the proposal is left null.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- The requested Elsa user does not exist.
- The requested Elsa user is outside the target tenant.
- A unique Elsa user name could not be reserved for the…
- This external identity provisioner does not support atomic…
- A configured default role no longer exists.
AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13).
Data as JSON: /api/errors/ff5560d4ee80b5a2.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.ExternalAuthentication/Services/ExternalIdentityUserProvisioningService.cs:40
/// Resolves an explicitly selected user or creates a credential-less user from the supplied proposal.
/// </summary>
public async ValueTask<(User User, bool WasCreated)> ResolveAsync(
ProvisioningRequest request,
Func<string, bool>? tryReserveUserName = null,
CancellationToken cancellationToken = default)
{
if (!string.IsNullOrWhiteSpace(request.ExistingUserId))
{
var existingUser = await userProvider.FindAsync(new()
{ Id = request.ExistingUserId }, cancellationToken)
?? throw new InvalidOperationException("The requested Elsa user does not exist.");
if (!string.Equals(existingUser.TenantId, request.TenantId, StringComparison.Ordinal))
throw new InvalidOperationException("The requested Elsa user is outside the target tenant.");
return (existingUser, false);
}
var proposal = request.Proposal ?? throw new InvalidOperationException("A user creation proposal is required for an unlinked external identity.");
var roleIds = await ResolveRoleIdsAsync(proposal.DefaultRoleIds, cancellationToken);
var prefix = NormalizeUserNamePrefix(proposal.UserNamePrefix);
for (var attempt = 0; attempt < MaximumUserNameAttempts; attempt++)
{
var name = $"{prefix}-{identityGenerator.GenerateId()}";
if (tryReserveUserName is not null && !tryReserveUserName(name))
continue;
if (await userProvider.FindAsync(new()
{ Name = name }, cancellationToken) is not null)
continue;
var user = new User
{
Id = identityGenerator.GenerateId(),
Name = name,
TenantId = request.TenantId,
HashedPassword = null,
HashedPasswordSalt = null,View on GitHub (pinned to fe9217bdfa)