{"record":{"id":"2b8daaeef17b95b7","repo":"elsa-workflows/elsa-core","slug":"a-conversation-id-is-required-parameter-conversation","errorCode":null,"errorMessage":"A conversation ID is required. (Parameter 'conversation')","messagePattern":"A conversation ID is required\\. \\(Parameter 'conversation'\\)","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/modules/Elsa.AI.Host/Services/InMemoryAIConversationStore.cs","lineNumber":71,"sourceCode":"        var expiresAt = conversation.RetentionExpiresAt;\n        return expiresAt.HasValue && expiresAt <= DateTimeOffset.UtcNow;\n    }\n\n    private static void ValidateOwnership(AIConversation existing, AIConversation conversation)\n    {\n        if (!string.Equals(NormalizeTenantId(existing.TenantId), NormalizeTenantId(conversation.TenantId), StringComparison.Ordinal))\n            throw new InvalidOperationException(\"Cannot overwrite an AI conversation that belongs to another tenant.\");\n\n        if (!string.IsNullOrWhiteSpace(existing.UserId) && !string.Equals(existing.UserId, conversation.UserId, StringComparison.Ordinal))\n            throw new InvalidOperationException(\"Cannot overwrite an AI conversation that belongs to another user.\");\n    }\n\n    private static string NormalizeTenantId(string? tenantId) => tenantId ?? \"\";\n\n    private static void Validate(AIConversation conversation)\n    {\n        if (string.IsNullOrWhiteSpace(conversation.Id))\n            throw new ArgumentException(\"A conversation ID is required.\", nameof(conversation));\n\n        if (string.IsNullOrWhiteSpace(conversation.UserId))\n            throw new ArgumentException(\"A conversation user ID is required.\", nameof(conversation));\n    }\n}\n","sourceCodeStart":53,"sourceCodeEnd":77,"githubUrl":"https://github.com/elsa-workflows/elsa-core/blob/fe9217bdfa0e27f0e09e45006eb6898f616e513d/src/modules/Elsa.AI.Host/Services/InMemoryAIConversationStore.cs#L53-L77","documentation":"InMemoryAIConversationStore.Validate requires every conversation passed to SaveAsync to have a non-whitespace Id. An empty or null Id makes the record unaddressable, so an ArgumentException naming the 'conversation' parameter is thrown before anything is stored.","triggerScenarios":"Calling SaveAsync with an AIConversation whose Id is null, empty, or whitespace.","commonSituations":"Constructing conversations from deserialized payloads missing the Id; relying on a default (empty) Id field; mapping code that drops the conversation ID during transformation.","solutions":["Populate conversation.Id before calling SaveAsync (e.g. Guid.NewGuid().ToString()).","Add a null/empty check on the source of the conversation Id in your code.","Use the store's conversation-creation API that assigns an Id instead of constructing manually."],"exampleFix":"// before\nawait store.SaveAsync(new AIConversation { UserId = user });\n// after\nawait store.SaveAsync(new AIConversation { Id = Guid.NewGuid().ToString(), UserId = user });","handlingStrategy":"validation","validationCode":"if (string.IsNullOrWhiteSpace(conversation.Id)) throw new ArgumentException(\"Conversation Id required\");","typeGuard":"bool HasId(AIConversation c) => !string.IsNullOrWhiteSpace(c.Id);","tryCatchPattern":"try { await store.SaveAsync(conversation); } catch (ArgumentException ex) when (ex.ParamName == \"conversation\") { logger.LogError(ex, \"Conversation missing Id/UserIds\"); }","preventionTips":["Generate an Id (Guid) at conversation creation time.","Validate required fields in your own mapping layer before calling the store.","Use factory methods that always set Id."],"tags":["dotnet","validation","argument","in-memory-store"],"backgroundTag":"missing-required-argument","analyzedSha":"fe9217bdfa0e27f0e09e45006eb6898f616e513d","analyzedAt":"2026-09-13T20:32:34.702Z","contentChangedAt":"2026-09-13T20:32:34.702Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}