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
- Only mutate line items while Status is Draft; create a credit/adjustment for issued invoices.
- Check Status before calling Issue() or AddLineItem().
- Make event handlers idempotent so duplicate events don't re-apply mutations.
- 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
- Edit line items only while Draft
- Design duplicate-event handlers to be idempotent
- Clone a new draft instead of mutating issued invoices
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
- Cannot mark invoice as paid from status
- Paid invoices cannot be voided.
- Top-up request must be
- Insufficient wallet balance.
- Top-up request cannot be rejected because it is (only…
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)