kgrzybek/modular-monolith-with-ddd · error · InvalidCommandException
Meeting comment for removing must exist.
Error message
Meeting comment for removing must exist.
What it means
Thrown by RemoveMeetingCommentCommandHandler when GetByIdAsync returns null for the MeetingCommentId. Removal needs an existing comment (and uses it to resolve meeting and group), so a missing comment aborts with InvalidCommandException (HTTP 400).
Source
Thrown at src/Modules/Meetings/Application/MeetingComments/RemoveMeetingComment/RemoveMeetingCommentCommandHandler.cs:30
private readonly IMeetingCommentRepository _meetingCommentRepository;
private readonly IMeetingRepository _meetingRepository;
private readonly IMeetingGroupRepository _meetingGroupRepository;
private readonly IMemberContext _memberContext;
internal RemoveMeetingCommentCommandHandler(IMeetingCommentRepository meetingCommentRepository, IMeetingRepository meetingRepository, IMeetingGroupRepository meetingGroupRepository, IMemberContext memberContext)
{
_meetingCommentRepository = meetingCommentRepository;
_meetingRepository = meetingRepository;
_meetingGroupRepository = meetingGroupRepository;
_memberContext = memberContext;
}
public async Task Handle(RemoveMeetingCommentCommand command, CancellationToken cancellationToken)
{
var meetingComment = await _meetingCommentRepository.GetByIdAsync(new MeetingCommentId(command.MeetingCommentId));
if (meetingComment == null)
{
throw new InvalidCommandException(["Meeting comment for removing must exist."]);
}
var meeting = await _meetingRepository.GetByIdAsync(meetingComment.GetMeetingId());
var meetingGroup = await _meetingGroupRepository.GetByIdAsync(meeting.GetMeetingGroupId());
meetingComment.Remove(_memberContext.MemberId, meetingGroup, command.Reason);
}
}
}View on GitHub (pinned to 91c8ef24b4)
Solutions
- Make remove idempotent on the client side: treat 'not found' as already removed.
- Pre-check existence and return 404/204 instead of dispatching a doomed command.
- Debounce/dedupe the remove button to avoid duplicate dispatches.
- Map InvalidCommandException to 400/404 at the API boundary.
Example fix
// before await _commandDispatcher.SendAsync(new RemoveMeetingCommentCommand(commentId, reason)); // after var comment = await _meetingQueries.GetCommentAsync(commentId); if (comment is null) return NoContent(); // already removed await _commandDispatcher.SendAsync(new RemoveMeetingCommentCommand(commentId, reason));
Defensive patterns
Strategy: validation
Validate before calling
var comment = await _meetingQueries.GetCommentAsync(commentId); if (comment is null) return NoContent(); // already removed await _commandDispatcher.SendAsync(new RemoveMeetingCommentCommand(commentId, reason));
Type guard
public static bool IsValidRemoveCommand(RemoveMeetingCommentCommand c) =>
c.MeetingCommentId != Guid.Empty && !string.IsNullOrWhiteSpace(c.Reason); Try / catch
try { await _commandDispatcher.SendAsync(cmd); }
catch (InvalidCommandException ex) when (ex.Errors.Any(m => m.Contains("must exist")))
{ return NoContent(); } // idempotent remove Prevention
- Treat a missing comment on remove as success (idempotent).
- Dedupe the remove button to avoid duplicate dispatches.
- Require a non-empty reason client-side.
When it happens
Trigger: Dispatching RemoveMeetingCommentCommand with a comment id that does not exist: already removed, wrong id, or duplicate removal request.
Common situations: User double-submits a remove action; background job retries removal of an already-deleted comment; client shows a ghost comment.
Related errors
- Meeting for adding comment must exist.
- To add like the comment must exist.
- To create reply the comment must exist.
- Meeting comment for editing must exist.
- Meeting comment like for removing must exist.
AI-assisted analysis of kgrzybek/modular-monolith-with-ddd@91c8ef24b4 (2026-08-13).
Data as JSON: /api/errors/acf70a260cba56b1.
Report an issue: GitHub.