fullstackhero/dotnet-starter-kit · error · InvalidOperationException

Operation requires invoice status

Error message

Operation requires invoice status {expected} but was {Status}.

What it means

Invoice.RequireStatus is a private guard used by AddLineItem and Issue: the operation demands the invoice be in exactly the expected status (e.g. Draft) and throws InvalidOperationException otherwise. It enforces that line items are only edited before issuance and that Issue only applies to drafts.

Solutions

  1. Only mutate line items while Status is Draft; create a credit/adjustment for issued invoices.
  2. Check Status before calling Issue() or AddLineItem().
  3. Make event handlers idempotent so duplicate events don't re-apply mutations.
  4. Clone a new Draft invoice if changes are needed after issuance.

Example fix

// before
issuedInvoice.AddLineItem(item); // throws

// after
if (invoice.Status == InvoiceStatus.Draft)
{
    invoice.AddLineItem(item);
}
Defensive patterns

Strategy: validation

Validate before calling

if (invoice.Status != InvoiceStatus.Draft) throw new InvalidOperationException("Invoice lines are immutable after issue.");

Try / catch

try { invoice.AddLineItem(item); } catch (InvalidOperationException ex) { // offer 'create revised invoice' flow }

Prevention

When it happens

Trigger: Calling AddLineItem on an Issued/Paid/Void invoice; calling Issue() on an invoice that is not Draft.

Common situations: Re-running an invoice-build job against already-issued invoices; editing invoice lines after sending to the customer; replayed integration events applying mutations twice.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15). Data as JSON: /api/errors/7f45c0187a90ba3f. Report an issue: GitHub.

Appendix: source

Thrown at src/Modules/Billing/Modules.Billing/Domain/Invoice.cs:174

        }
        Status = InvoiceStatus.Void;
        VoidedAtUtc = DateTime.UtcNow;
        if (!string.IsNullOrWhiteSpace(reason))
        {
            Notes = string.IsNullOrWhiteSpace(Notes) ? reason : $"{Notes}; Voided: {reason}";
        }
    }

    public void SetNotes(string? notes)
    {
        Notes = notes;
    }

    private void RequireStatus(InvoiceStatus expected)
    {
        if (Status != expected)
        {
            throw new InvalidOperationException($"Operation requires invoice status {expected} but was {Status}.");
        }
    }

    private void RecalculateTotals()
    {
        SubtotalAmount = _lineItems.Aggregate(
            Money.Zero(SubtotalAmount.Currency),
            (acc, l) => acc.Add(l.Amount));
    }
}

View on GitHub (pinned to 3f2959e683)