fullstackhero/dotnet-starter-kit · error · CustomException

A closed ticket cannot accept new comments — reopen it…

Error message

A closed ticket cannot accept new comments — reopen it first.

What it means

Ticket.AddComment rejects new comments on a Closed ticket, throwing CustomException with HTTP 409 Conflict. Commenting is only allowed while the ticket is active; it must be reopened to continue the discussion. This guards the closed ticket's frozen conversation thread.

Solutions

  1. Reopen the ticket, then add the comment.
  2. Handle 409 in the client, refresh the ticket, and show 'Reopen to comment'.
  3. Check ticket status when loading the comment form and hide/disable it for closed tickets.
  4. If the reply must be preserved, reopen the ticket programmatically before submitting the comment.

Example fix

// before
ticket.AddComment(authorUserId, body); // 409 if closed
// after
if (ticket.Status == TicketStatus.Closed)
{
    ticket.Reopen();
}
ticket.AddComment(authorUserId, body);
Defensive patterns

Strategy: validation

Validate before calling

if (ticket.status === TicketStatus.Closed) {
  throw new Error('Reopen the ticket to comment.');
}
ticket.addComment(authorUserId, body);

Type guard

bool CanComment(Ticket t) => t.Status != TicketStatus.Closed;

Try / catch

try
{
    await api.addComment(ticketId, body);
}
catch (err) {
  if (err.status === 409) {
    await api.reopen(ticketId);
    await api.addComment(ticketId, body);
  } else { throw err; }
}

Prevention

When it happens

Trigger: Calling AddComment (or the add-comment endpoint) on a ticket with Status == TicketStatus.Closed, e.g. replying from a stale comment form after the ticket was closed.

Common situations: User kept a comment box open while an agent closed the ticket; email/automation piping replies into closed tickets; UI not refreshing ticket status after close.

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/e79bdb1fe281ed50. Report an issue: GitHub.

Appendix: source

Thrown at src/Modules/Tickets/Modules.Tickets/Domain/Ticket.cs:201

        {
            return;
        }

        // Clear the close timestamp so a fresh resolution gets its own audit window.
        ClosedAtUtc = null;
        ResolvedAtUtc = null;
        ResolutionNote = null;
        UpdatedAtUtc = DateTime.UtcNow;
        // Reopened tickets fall back to whatever assignment state they had:
        // if still assigned, InProgress; if not, Open.
        TransitionStatus(AssignedToUserId is null ? TicketStatus.Open : TicketStatus.InProgress);
    }

    public Guid AddComment(Guid authorUserId, string body)
    {
        if (Status == TicketStatus.Closed)
        {
            throw new CustomException(
                "A closed ticket cannot accept new comments — reopen it first.",
                (IEnumerable<string>?)null,
                HttpStatusCode.Conflict);
        }

        var comment = TicketComment.Create(Id, authorUserId, body);
        _comments.Add(comment);
        UpdatedAtUtc = DateTime.UtcNow;

        AddDomainEvent(DomainEvent.Create<TicketCommentAddedDomainEvent>(
            (id, ts) => new TicketCommentAddedDomainEvent(Id, comment.Id, authorUserId, id, ts)));

        return comment.Id;
    }

    private void TransitionStatus(TicketStatus next)
    {
        if (next == Status)

View on GitHub (pinned to 3f2959e683)