fullstackhero/dotnet-starter-kit · error · InvalidOperationException
Top-up request must be
Error message
Top-up request must be {expected} (was {Status}). What it means
TopupRequest.Require is the private state guard called by MarkInvoiced, MarkCompleted, and Reject: the wallet top-up request must currently be in the exact expected status (e.g. Pending) for the transition to be legal, otherwise InvalidOperationException is thrown.
Solutions
- Check the request's current Status before invoking the transition method.
- Make the calling handler idempotent: swallow the transition when already in the target state.
- Reject competing transitions early (e.g. cancel the reject path once invoiced).
- Persist status changes in a single transaction to avoid concurrent transitions.
Example fix
// before
request.MarkCompleted(); // throws if not Invoiced
// after
if (request.Status == TopupRequestStatus.Invoiced)
{
request.MarkCompleted();
} Defensive patterns
Strategy: validation
Validate before calling
if (request.Status != TopupRequestStatus.Pending) return; // already processed
Try / catch
try { topup.MarkInvoiced(invoiceRef); } catch (InvalidOperationException ex) { logger.LogInformation(ex, "Top-up already transitioned"); } Prevention
- Guard approval UIs against double-clicks
- Check current Status before each transition
- Process approve/reject events with idempotency keys
When it happens
Trigger: Calling MarkInvoiced on a request already invoiced/completed; MarkCompleted on a pending-but-uninvoiced request; Reject on a completed request; duplicate webhook/event handling driving the same transition twice.
Common situations: Approval UI double-clicks; retrying a failed job after the first attempt already advanced the status; out-of-order processing of approve/reject events.
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.
- Operation requires invoice status
- 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/e9b25c3526831fcd.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Billing/Modules.Billing/Domain/TopupRequest.cs:64
public void MarkCompleted()
{
Require(TopupRequestStatus.Invoiced);
Status = TopupRequestStatus.Completed;
CompletedAtUtc = DateTime.UtcNow;
}
public void Reject(string? reason)
{
Require(TopupRequestStatus.Pending);
DecisionNote = reason;
Status = TopupRequestStatus.Rejected;
DecidedAtUtc = DateTime.UtcNow;
}
private void Require(TopupRequestStatus expected)
{
if (Status != expected)
throw new InvalidOperationException($"Top-up request must be {expected} (was {Status}).");
}
}
View on GitHub (pinned to 3f2959e683)