{"record":{"id":"84643a0cc4cf61ef","repo":"elsa-workflows/elsa-core","slug":"a-user-task-title-is-required","errorCode":null,"errorMessage":"A User Task title is required.","messagePattern":"A User Task title is required\\.","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/modules/Elsa.UserTasks/Models/UserTaskModels.cs","lineNumber":207,"sourceCode":"    public bool AllowManagerExclusionOverride { get; init; }\n    public int Priority { get; init; } = 50;\n    public DateTimeOffset? DueAt { get; init; }\n    public string? Instructions { get; init; }\n    public JsonElement? TaskData { get; init; }\n    public UserTaskFormReference? FormReference { get; init; }\n    public IReadOnlyCollection<UserTaskAction> Actions { get; init; } = [];\n    public IReadOnlyCollection<UserTaskInvitationDefinition> Invitations { get; init; } = [];\n    public bool EnableTimeoutOutcome { get; init; }\n    public bool EnableCancellationOutcome { get; init; }\n\n    public UserTaskDefinitionSnapshot Normalize()\n    {\n        var actions = Actions.Count == 0\n            ? [new UserTaskAction(\"Complete\", \"Complete\")]\n            : Actions;\n\n        if (string.IsNullOrWhiteSpace(Title))\n            throw new ArgumentException(\"A User Task title is required.\", nameof(Title));\n        if (actions.Any(x => string.IsNullOrWhiteSpace(x.Key) || string.IsNullOrWhiteSpace(x.Label)))\n            throw new ArgumentException(\"User Task action keys and labels are required.\", nameof(Actions));\n        if (actions.Any(x => string.Equals(x.Key, \"Timeout\", StringComparison.OrdinalIgnoreCase) || string.Equals(x.Key, \"Cancelled\", StringComparison.OrdinalIgnoreCase)))\n            throw new ArgumentException(\"Timeout and Cancelled are reserved User Task action keys.\");\n        if (actions.Select(x => x.Key).Distinct(StringComparer.OrdinalIgnoreCase).Count() != actions.Count)\n            throw new ArgumentException(\"User Task action keys must be unique.\");\n        if (Priority is < 0 or > 100)\n            throw new ArgumentOutOfRangeException(nameof(Priority), \"Priority must be between 0 and 100.\");\n        if (Invitations.Any(x => string.IsNullOrWhiteSpace(x.VerifierName) || x.AllowedActions.Count == 0 || x.AllowedActions.Any(string.IsNullOrWhiteSpace)))\n            throw new ArgumentException(\"Invitation verifier names and allowed actions are required.\", nameof(Invitations));\n        if (Invitations.Any(invitation => invitation.AllowedActions.Any(allowed => !actions.Any(action => string.Equals(action.Key, allowed, StringComparison.OrdinalIgnoreCase)))))\n            throw new ArgumentException(\"Invitation actions must be configured User Task actions.\", nameof(Invitations));\n\n        return this with { Actions = actions };\n    }\n}\n\npublic sealed class UserTask","sourceCodeStart":189,"sourceCodeEnd":225,"githubUrl":"https://github.com/elsa-workflows/elsa-core/blob/fe9217bdfa0e27f0e09e45006eb6898f616e513d/src/modules/Elsa.UserTasks/Models/UserTaskModels.cs#L189-L225","documentation":"UserTask.Normalize validates and defaults the task model before persistence. Every user task must have a non-whitespace Title; Normalize throws ArgumentException naming the Title parameter when it is missing. This is a model-invariant check performed when the task record is built.","triggerScenarios":"Constructing or completing a UserTask with Title null, empty, or whitespace-only and then invoking Normalize (directly or via create/save flows).","commonSituations":"UI forms that allow blank titles; API clients omitting the title field; deserialization from external data sources where the title column was empty.","solutions":["Provide a non-empty Title when creating the UserTask.","Default the title from another field (e.g., activity/display name) if a blank value is acceptable in your domain.","Validate at the API/DTO boundary before mapping into the UserTask model."],"exampleFix":"// before\nvar task = new UserTask { Actions = { new UserTaskAction(\"Approve\", \"Approve\") } };\ntask.Normalize(); // throws\n// after\nvar task = new UserTask { Title = \"Review pull request\", Actions = { new UserTaskAction(\"Approve\", \"Approve\") } };\ntask.Normalize();","handlingStrategy":"validation","validationCode":"if (string.IsNullOrWhiteSpace(task.Title))\n    return Results.ValidationProblem(new Dictionary<string, string[]> { [nameof(UserTask.Title)] = [\"Title is required.\"] });","typeGuard":"bool HasTitle(UserTask t) => !string.IsNullOrWhiteSpace(t.Title);","tryCatchPattern":"try\n{\n    task.Normalize();\n}\ncatch (ArgumentException ex) when (ex.ParamName == nameof(UserTask.Title))\n{\n    return Results.ValidationProblem(new { title = new[] { ex.Message } });\n}","preventionTips":["Make Title a required field in create/update DTOs.","Default the title from workflow activity display names when not user-supplied.","Trim inputs before assignment to avoid whitespace-only titles."],"tags":["usertasks","validation","model"],"backgroundTag":"empty-required-field","analyzedSha":"fe9217bdfa0e27f0e09e45006eb6898f616e513d","analyzedAt":"2026-09-13T20:32:34.702Z","contentChangedAt":"2026-09-13T20:32:34.702Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}