elsa-workflows/elsa-core · error · InvalidOperationException
Cannot overwrite an AI conversation that belongs to another…
Error message
Cannot overwrite an AI conversation that belongs to another user.
What it means
InMemoryAIConversationStore.SaveAsync rejects overwriting an existing conversation whose UserId differs from the incoming one, when the stored record has a non-empty UserId. This prevents one user from replacing another user's conversation. Thrown from ValidateOwnership during SaveAsync.
Solutions
- Ensure the same UserId is supplied when updating an existing conversation.
- Use a fresh conversation ID for a different user.
- Delete the existing conversation first if reassignment is intended.
- Check that the current-user provider returns a stable UserId across calls.
Example fix
// before
await store.SaveAsync(new AIConversation { Id = id, TenantId = tenant, UserId = "user-b" });
// after
await store.SaveAsync(new AIConversation { Id = id, TenantId = tenant, UserId = "user-a" }); Defensive patterns
Strategy: validation
Validate before calling
var existing = await store.FindAsync(conversation.Id);
if (existing is not null && !string.IsNullOrWhiteSpace(existing.UserId) && existing.UserId != conversation.UserId)
throw new InvalidOperationException("User mismatch for conversation " + conversation.Id); Type guard
bool SameUser(AIConversation? existing, AIConversation c) => existing is null || string.IsNullOrWhiteSpace(existing.UserId) || existing.UserId == c.UserId;
Try / catch
try { await store.SaveAsync(conversation); } catch (InvalidOperationException ex) when (ex.Message.Contains("another user")) { logger.LogWarning(ex, "Cross-user save blocked for {Id}", conversation.Id); } Prevention
- Load the existing conversation and reuse its UserId on updates.
- Keep the authenticated user ID stable for the conversation lifetime.
- Test user-switch scenarios against the store.
When it happens
Trigger: Calling SaveAsync with a conversation whose Id matches an existing record but whose UserId differs from the stored UserId.
Common situations: Switching authenticated users in tests without clearing the store; user ID mismatches after auth refactors; replaying conversation saves under a different identity.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- Cannot overwrite an AI conversation that belongs to another…
- Cannot overwrite an AI conversation that belongs to another…
- ' ' is not a well-formed permission. Expected ' : '.
- 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/270d77873f06ab6f.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.AI.Host/Services/InMemoryAIConversationStore.cs:63
private bool IsExpired(AIConversation conversation)
{
if (conversation.RetentionMode == AIRetentionMode.Ephemeral)
return conversation.Status is AIConversationStatus.Completed or AIConversationStatus.Failed;
if (conversation.RetentionMode == AIRetentionMode.Durable)
return false;
var expiresAt = conversation.RetentionExpiresAt;
return expiresAt.HasValue && expiresAt <= DateTimeOffset.UtcNow;
}
private static void ValidateOwnership(AIConversation existing, AIConversation conversation)
{
if (!string.Equals(NormalizeTenantId(existing.TenantId), NormalizeTenantId(conversation.TenantId), StringComparison.Ordinal))
throw new InvalidOperationException("Cannot overwrite an AI conversation that belongs to another tenant.");
if (!string.IsNullOrWhiteSpace(existing.UserId) && !string.Equals(existing.UserId, conversation.UserId, StringComparison.Ordinal))
throw new InvalidOperationException("Cannot overwrite an AI conversation that belongs to another user.");
}
private static string NormalizeTenantId(string? tenantId) => tenantId ?? "";
private static void Validate(AIConversation conversation)
{
if (string.IsNullOrWhiteSpace(conversation.Id))
throw new ArgumentException("A conversation ID is required.", nameof(conversation));
if (string.IsNullOrWhiteSpace(conversation.UserId))
throw new ArgumentException("A conversation user ID is required.", nameof(conversation));
}
}
View on GitHub (pinned to fe9217bdfa)