elsa-workflows/elsa-core · error · ArgumentException

A proposal conversation ID is required.

Error message

A proposal conversation ID is required.

What it means

The proposal store's Validate method requires a non-whitespace ConversationId on every AIProposal. Proposals are scoped to their parent conversation; a missing ConversationId breaks that linkage and tenant/user ownership checks, so ArgumentException (paramName 'proposal') is thrown.

Solutions

  1. Set proposal.ConversationId to the parent conversation's Id before SaveAsync.
  2. Ensure the proposal is created through a flow that has the conversation context available.
  3. Validate in your service layer that ConversationId is present and the referenced conversation exists.

Example fix

// before
var proposal = new AIProposal { Id = id, CreatedBy = user }; // ConversationId missing
await proposalStore.SaveAsync(proposal);
// after
var proposal = new AIProposal { Id = id, ConversationId = conversation.Id, CreatedBy = user };
await proposalStore.SaveAsync(proposal);
Defensive patterns

Strategy: validation

Validate before calling

if (string.IsNullOrWhiteSpace(proposal.ConversationId)) throw new ArgumentException("A proposal ConversationId is required before SaveAsync.");
// optionally: ensure the conversation exists
// if (!await dbContext.Conversations.AnyAsync(c => c.Id == proposal.ConversationId)) throw new InvalidOperationException("Parent conversation not found.");

Type guard

static bool HasConversation(AIProposal p) => !string.IsNullOrWhiteSpace(p.ConversationId);

Try / catch

try { await proposalStore.SaveAsync(proposal); }
catch (ArgumentException ex) when (ex.ParamName == "proposal" && ex.Message.Contains("conversation ID")) { /* attach the proposal to a conversation before saving */ }

Prevention

When it happens

Trigger: Calling SaveAsync with an AIProposal that has a valid Id but a null/empty/whitespace ConversationId (this check runs after the Id check and before the CreatedBy check).

Common situations: Creating a proposal outside the context of a conversation; a mapper that omits ConversationId; API callers posting proposals without the parent conversation reference; refactors renaming the field and missing the assignment.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13). Data as JSON: /api/errors/a7c0f2157679fa31. Report an issue: GitHub.

Appendix: source

Thrown at src/modules/Elsa.AI.Persistence.EFCore/Stores/EFCoreAIProposalStore.cs:135

    private static bool BelongsToTenant(string? storedTenantId, string? requestedTenantId) =>
        string.Equals(NormalizeTenantId(storedTenantId), NormalizeTenantId(requestedTenantId), StringComparison.Ordinal);

    private static string NormalizeTenantId(string? tenantId) => tenantId ?? "";

    private static void ValidateUserOwnership(AIProposalRecord record, AIProposal proposal)
    {
        if (!string.IsNullOrWhiteSpace(record.CreatedBy) && !string.Equals(record.CreatedBy, proposal.CreatedBy, StringComparison.Ordinal))
            throw new InvalidOperationException("Cannot overwrite an AI proposal that belongs to another user.");
    }

    private static void Validate(AIProposal proposal)
    {
        if (string.IsNullOrWhiteSpace(proposal.Id))
            throw new ArgumentException("A proposal ID is required.", nameof(proposal));

        if (string.IsNullOrWhiteSpace(proposal.ConversationId))
            throw new ArgumentException("A proposal conversation ID is required.", nameof(proposal));

        if (string.IsNullOrWhiteSpace(proposal.CreatedBy))
            throw new ArgumentException("A proposal creator is required.", nameof(proposal));
    }
}

View on GitHub (pinned to fe9217bdfa)