fullstackhero/dotnet-starter-kit · error · CustomException
Only a resolved ticket can be closed — current status is
Error message
Only a resolved ticket can be closed — current status is {Status}. Resolve it first. What it means
Ticket.Close enforces that only a Resolved ticket can be closed; closing from any other status throws CustomException with HTTP 409 Conflict and reports the current status. Closing is the terminal step of the Open → Resolved → Closed workflow.
Solutions
- Call Resolve(resolutionNote) first, then Close — the workflow requires Resolved before Closed.
- Reload the ticket and check Status before closing.
- Make client close actions conditional: only enable Close when status is Resolved.
- Handle the 409 in the client by refreshing the ticket and prompting for the Resolve step.
Example fix
// before
ticket.Close(); // 409 unless Resolved
// after
if (ticket.Status == TicketStatus.Open)
{
ticket.Resolve("Auto-resolved before close");
}
ticket.Close(); Defensive patterns
Strategy: validation
Validate before calling
if (ticket.status !== TicketStatus.Resolved) {
throw new Error(`Ticket must be Resolved before closing (current: ${ticket.status}).`);
}
ticket.close(); Type guard
bool CanClose(Ticket t) => t.Status == TicketStatus.Resolved;
Try / catch
try
{
ticket.Close();
}
catch (CustomException ex) when (ex.StatusCode == HttpStatusCode.Conflict)
{
ticket.Resolve("Resolved prior to close");
ticket.Close();
} Prevention
- Follow the workflow: Resolve before Close; never close from Open.
- Enable the Close action in the UI only when status is Resolved.
- Refresh the ticket before closing if the page has been idle.
- In automation, transition through Resolved explicitly rather than jumping to Close.
When it happens
Trigger: Calling Close on a ticket whose Status is Open (or any status other than Resolved/Closed). Closed tickets return silently (idempotent); everything else throws.
Common situations: Support agent closing a ticket without first marking it resolved; client skipping the Resolve step; stale UI showing Resolved while the ticket was reopened; batch scripts that close tickets without transitioning them through Resolved.
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
- A closed ticket cannot be resolved — reopen it first.
- A closed ticket cannot accept new comments — reopen it…
- Cannot a ticket in status — reopen it first.
- A closed ticket cannot be edited — reopen it first.
- 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/03a48dd46b9beee2.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Tickets/Modules.Tickets/Domain/Ticket.cs:148
ResolutionNote = string.IsNullOrWhiteSpace(resolutionNote) ? null : resolutionNote.Trim();
ResolvedAtUtc = DateTime.UtcNow;
UpdatedAtUtc = DateTime.UtcNow;
TransitionStatus(TicketStatus.Resolved);
}
/// <summary>
/// Finalizes a resolved ticket (Resolved → Closed). Idempotent when already Closed;
/// rejects any other source state with a 409 so the documented state machine holds.
/// </summary>
public void Close()
{
if (Status == TicketStatus.Closed)
{
return;
}
if (Status != TicketStatus.Resolved)
{
throw new CustomException(
$"Only a resolved ticket can be closed — current status is {Status}. Resolve it first.",
(IEnumerable<string>?)null,
HttpStatusCode.Conflict);
}
ClosedAtUtc = DateTime.UtcNow;
UpdatedAtUtc = DateTime.UtcNow;
TransitionStatus(TicketStatus.Closed);
}
/// <summary>
/// Edits the mutable details of an open/in-progress/resolved ticket. A closed ticket is
/// frozen — it must be reopened first.
/// </summary>
public void UpdateDetails(string title, string? description, TicketPriority priority)
{
ArgumentException.ThrowIfNullOrWhiteSpace(title);
if (Status == TicketStatus.Closed)View on GitHub (pinned to 3f2959e683)