elsa-workflows/elsa-core · error · ArgumentException
User Task action keys and labels are required.
Error message
User Task action keys and labels are required.
What it means
UserTask.Normalize requires every action in the Actions collection to have both a non-whitespace Key and a non-whitespace Label. If any action fails this check, Normalize throws ArgumentException pointing at the Actions parameter. This guarantees persisted tasks always render usable action buttons.
Solutions
- Ensure every UserTaskAction has both Key and Label set before calling Normalize.
- Filter out incomplete actions instead of passing them through.
- Validate the incoming DTO action list at the API boundary.
Example fix
// before
task.Actions.Add(new UserTaskAction("Approve", null)); // label missing
task.Normalize(); // throws
// after
task.Actions.Add(new UserTaskAction("Approve", "Approve"));
task.Normalize(); Defensive patterns
Strategy: validation
Validate before calling
var invalid = task.Actions.Where(a => string.IsNullOrWhiteSpace(a.Key) || string.IsNullOrWhiteSpace(a.Label)).ToList();
if (invalid.Count > 0)
return Results.ValidationProblem(new { actions = new[] { "All action keys and labels are required." } }); Type guard
bool HasValidActions(UserTask t) =>
t.Actions.All(a => !string.IsNullOrWhiteSpace(a.Key) && !string.IsNullOrWhiteSpace(a.Label)); Try / catch
try
{
task.Normalize();
}
catch (ArgumentException ex) when (ex.ParamName == nameof(UserTask.Actions))
{
return Results.ValidationProblem(new { actions = new[] { ex.Message } });
} Prevention
- Enforce Key+Label in the UI when users add custom actions.
- Filter out incomplete actions before persisting.
- Reject action payloads at the DTO mapping stage rather than relying on Normalize.
When it happens
Trigger: Calling Normalize (directly or via create/save) with an Actions list containing a UserTaskAction whose Key or Label is null, empty, or whitespace.
Common situations: Dynamically generated action lists where some entries lack labels; API payloads with incomplete action objects; bulk-imported task definitions with placeholder actions.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- A User Task title is required.
- A configuration key is required.
- A conversation ID is required. (Parameter 'conversation')
- A conversation ID is required. (Parameter 'conversation')
- A conversation user ID is required. (Parameter…
AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13).
Data as JSON: /api/errors/679f73b55e98d36f.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.UserTasks/Models/UserTaskModels.cs:209
public DateTimeOffset? DueAt { get; init; }
public string? Instructions { get; init; }
public JsonElement? TaskData { get; init; }
public UserTaskFormReference? FormReference { get; init; }
public IReadOnlyCollection<UserTaskAction> Actions { get; init; } = [];
public IReadOnlyCollection<UserTaskInvitationDefinition> Invitations { get; init; } = [];
public bool EnableTimeoutOutcome { get; init; }
public bool EnableCancellationOutcome { get; init; }
public UserTaskDefinitionSnapshot Normalize()
{
var actions = Actions.Count == 0
? [new UserTaskAction("Complete", "Complete")]
: Actions;
if (string.IsNullOrWhiteSpace(Title))
throw new ArgumentException("A User Task title is required.", nameof(Title));
if (actions.Any(x => string.IsNullOrWhiteSpace(x.Key) || string.IsNullOrWhiteSpace(x.Label)))
throw new ArgumentException("User Task action keys and labels are required.", nameof(Actions));
if (actions.Any(x => string.Equals(x.Key, "Timeout", StringComparison.OrdinalIgnoreCase) || string.Equals(x.Key, "Cancelled", StringComparison.OrdinalIgnoreCase)))
throw new ArgumentException("Timeout and Cancelled are reserved User Task action keys.");
if (actions.Select(x => x.Key).Distinct(StringComparer.OrdinalIgnoreCase).Count() != actions.Count)
throw new ArgumentException("User Task action keys must be unique.");
if (Priority is < 0 or > 100)
throw new ArgumentOutOfRangeException(nameof(Priority), "Priority must be between 0 and 100.");
if (Invitations.Any(x => string.IsNullOrWhiteSpace(x.VerifierName) || x.AllowedActions.Count == 0 || x.AllowedActions.Any(string.IsNullOrWhiteSpace)))
throw new ArgumentException("Invitation verifier names and allowed actions are required.", nameof(Invitations));
if (Invitations.Any(invitation => invitation.AllowedActions.Any(allowed => !actions.Any(action => string.Equals(action.Key, allowed, StringComparison.OrdinalIgnoreCase)))))
throw new ArgumentException("Invitation actions must be configured User Task actions.", nameof(Invitations));
return this with { Actions = actions };
}
}
public sealed class UserTask
{
public string Id { get; set; } = Guid.NewGuid().ToString("N");View on GitHub (pinned to fe9217bdfa)