{"record":{"id":"f7e5ab016dcd1c80","repo":"elsa-workflows/elsa-core","slug":"stored-user-task-document-document-id-could-not-be","errorCode":null,"errorMessage":"Stored User Task document '{document.Id}' could not be deserialized.","messagePattern":"Stored User Task document '(.+?)' could not be deserialized\\.","errorType":"exception","errorClass":"DocumentStoreValidationException","httpStatus":null,"severity":"error","filePath":"src/modules/Elsa.UserTasks.Persistence.VNext/Repositories/VNextUserTaskRepository.cs","lineNumber":214,"sourceCode":"            [\"TaskType\"] = task.TaskType,\n            // Every index the schema provider declares must be supplied on save; the store rejects the\n            // write outright when one is absent, so an omission here disables the provider entirely\n            // rather than merely losing an index.\n            [\"WorkflowDefinitionId\"] = task.WorkflowDefinitionId,\n            [\"WorkflowInstanceId\"] = task.WorkflowInstanceId,\n            [\"ActivityInstanceId\"] = task.ActivityInstanceId,\n            [\"CreatedAt\"] = task.CreatedAt.ToString(\"O\", System.Globalization.CultureInfo.InvariantCulture),\n            [\"CompletedAt\"] = task.CompletedAt?.ToString(\"O\", System.Globalization.CultureInfo.InvariantCulture),\n            [\"AssigneeProvider\"] = task.Assignee?.Provider,\n            [\"AssigneeType\"] = task.Assignee?.Type.ToString(),\n            [\"AssigneeId\"] = task.Assignee?.Id,\n            [\"HealthSeverity\"] = task.HealthSeverity?.ToString(),\n            [\"Priority\"] = task.Priority.ToString(System.Globalization.CultureInfo.InvariantCulture),\n            [\"DueAt\"] = task.DueAt?.ToString(\"O\", System.Globalization.CultureInfo.InvariantCulture)\n        }, expectedVersion);\n\n    private static UserTask Deserialize(StoredDocument document) => JsonSerializer.Deserialize<UserTask>(document.Content, JsonOptions)\n        ?? throw new DocumentStoreValidationException($\"Stored User Task document '{document.Id}' could not be deserialized.\");\n\n    private static string DocumentId(string tenantId, string taskId) => $\"{tenantId}:{taskId}\";\n\n    private static bool Matches(UserTask task, UserTaskQuery query)\n    {\n        var search = query.Search?.Trim();\n        return task.TenantId == query.TenantId && (query.Statuses.Count == 0 || query.Statuses.Contains(task.Status)) &&\n               (!query.OnlyOverdue || task.IsOverdue) && (!query.OnlyWithoutDueDate || task.DueAt is null) &&\n               (string.IsNullOrWhiteSpace(query.TaskType) || task.TaskType == query.TaskType) &&\n               (!query.PriorityFrom.HasValue || task.Priority >= query.PriorityFrom) && (!query.PriorityTo.HasValue || task.Priority <= query.PriorityTo) &&\n               (!query.DueFrom.HasValue || task.DueAt >= query.DueFrom) && (!query.DueTo.HasValue || task.DueAt <= query.DueTo) &&\n               (string.IsNullOrWhiteSpace(query.WorkflowDefinitionId) || task.WorkflowDefinitionId == query.WorkflowDefinitionId) &&\n               (string.IsNullOrWhiteSpace(query.WorkflowInstanceId) || task.WorkflowInstanceId == query.WorkflowInstanceId) &&\n               (string.IsNullOrWhiteSpace(query.Reference) || task.Reference == query.Reference) &&\n               (string.IsNullOrWhiteSpace(search) || task.Title.Contains(search, StringComparison.OrdinalIgnoreCase) || task.Summary?.Contains(search, StringComparison.OrdinalIgnoreCase) == true || task.Reference?.Contains(search, StringComparison.OrdinalIgnoreCase) == true || task.TaskType?.Contains(search, StringComparison.OrdinalIgnoreCase) == true);\n    }\n\n    private static bool IsVisible(UserTask task, UserTaskQueryScope scope)","sourceCodeStart":196,"sourceCodeEnd":232,"githubUrl":"https://github.com/elsa-workflows/elsa-core/blob/fe9217bdfa0e27f0e09e45006eb6898f616e513d/src/modules/Elsa.UserTasks.Persistence.VNext/Repositories/VNextUserTaskRepository.cs#L196-L232","documentation":"Deserialize converts a stored document's JSON content back into a UserTask. If System.Text.Json returns null (e.g., the content deserializes to a null value rather than a valid UserTask object), the code throws DocumentStoreValidationException, indicating the stored document is corrupt or not the expected schema.","triggerScenarios":"Calling GetAsync or any LoadDocumentAsync-backed path on a document whose Content is 'null', empty, or deserializes to null under the repository's JsonOptions.","commonSituations":"Documents written by an older/other schema version; manual edits to the store; write failures that persisted null content; ID/tenant mapping bugs loading a foreign document.","solutions":["Inspect and repair or delete the corrupt document in the store.","Re-create the user task via the normal create flow.","Verify the serializer options used at write time match the reader (same JsonOptions/version)."],"exampleFix":"// before\nvar task = await repo.GetAsync(taskId, ct); // throws DocumentStoreValidationException\n// after\ntry {\n    var task = await repo.GetAsync(taskId, ct);\n}\ncatch (DocumentStoreValidationException ex) {\n    logger.LogError(ex, \"Corrupt user task document {TaskId}; recreating.\", taskId);\n    await repo.DeleteAsync(taskId, ct);\n    task = await repo.CreateAsync(newTask, ct);\n}","handlingStrategy":"try-catch","validationCode":"// validate stored content before deserializing yourself\nif (string.IsNullOrWhiteSpace(document.Content) || document.Content.Trim() == \"null\")\n    logger.LogError(\"User task document {DocumentId} has null/empty content.\", document.Id);","typeGuard":null,"tryCatchPattern":"try\n{\n    var task = await repo.GetAsync(taskId, ct);\n}\ncatch (DocumentStoreValidationException ex)\n{\n    logger.LogError(ex, \"Stored document for {TaskId} is corrupt.\", taskId);\n    // fall back to delete-and-recreate or surface an admin error\n}","preventionTips":["Never write null/empty content; always serialize a full UserTask.","Keep serializer options consistent between writes and reads across versions.","Add a store health check that round-trips a sample document."],"tags":["usertasks","deserialization","data-corruption"],"backgroundTag":"json-unmarshal-failed","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"}