elsa-workflows/elsa-core · error · ArgumentException

A proposal creator is required.

Error message

A proposal creator is required.

What it means

EFCoreAIProposalStore.Validate rejects an AIProposal that is missing required identity fields before writing it to the database. In this case proposal.CreatedBy is null or whitespace, meaning the proposal does not record who created it, which the store treats as a mandatory audit field. The exception is an ArgumentException naming the proposal parameter, thrown from Validate during SaveAsync.

Solutions

  1. Set proposal.CreatedBy to the acting user/service identity before calling SaveAsync.
  2. If the creator is unknown, use an explicit sentinel such as "system" rather than leaving it empty.
  3. Add upstream validation at the endpoint/handler level so unassigned proposals are rejected with a clear message before reaching the store.

Example fix

// before
var proposal = new AIProposal { Id = id, ConversationId = conversationId, Payload = payload };
await store.SaveAsync(proposal, ct);

// after
var proposal = new AIProposal { Id = id, ConversationId = conversationId, Payload = payload, CreatedBy = currentUser.Id };
await store.SaveAsync(proposal, ct);
Defensive patterns

Strategy: validation

Validate before calling

if (proposal is null) throw new ArgumentNullException(nameof(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));

Type guard

bool HasCreator(Elsa.AI.Models.AIProposal p) => !string.IsNullOrWhiteSpace(p?.CreatedBy);

Try / catch

try
{
    await store.SaveAsync(proposal, ct);
}
catch (ArgumentException ex) when (ex.ParamName == "proposal")
{
    logger.LogError(ex, "Invalid AI proposal: {Message}", ex.Message);
}

Prevention

When it happens

Trigger: Calling EFCoreAIProposalStore.SaveAsync(proposal, ct) where proposal.CreatedBy is null, empty, or whitespace, while proposal.Id and proposal.ConversationId are set.

Common situations: Constructing an AIProposal manually in code or tests without assigning CreatedBy; mapping from an API/DTO that omits the creator field; code paths that copy a proposal but drop the creator during transformation.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

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

    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)