kgrzybek/modular-monolith-with-ddd · error · InvalidCommandException

To create reply the comment must exist.

Error message

To create reply the comment must exist.

What it means

Thrown by AddReplyToMeetingCommentCommandHandler when GetByIdAsync for the InReplyToCommentId returns null. Replies require a parent comment; a missing parent rejects the command with InvalidCommandException (HTTP 400). The parent comment is also used to resolve the meeting, group, and commenting configuration.

Source

Thrown at src/Modules/Meetings/Application/MeetingComments/AddMeetingCommentReply/AddReplyToMeetingCommentCommandHandler.cs:33

        private readonly IMeetingGroupRepository _meetingGroupRepository;
        private readonly IMeetingCommentingConfigurationRepository _meetingCommentingConfigurationRepository;
        private readonly IMemberContext _memberContext;

        internal AddReplyToMeetingCommentCommandHandler(IMeetingCommentRepository meetingCommentRepository, IMeetingRepository meetingRepository, IMeetingGroupRepository meetingGroupRepository, IMeetingCommentingConfigurationRepository meetingCommentingConfigurationRepository, IMemberContext memberContext)
        {
            _meetingCommentRepository = meetingCommentRepository;
            _meetingRepository = meetingRepository;
            _meetingGroupRepository = meetingGroupRepository;
            _meetingCommentingConfigurationRepository = meetingCommentingConfigurationRepository;
            _memberContext = memberContext;
        }

        public async Task<Guid> Handle(AddReplyToMeetingCommentCommand command, CancellationToken cancellationToken)
        {
            var meetingComment = await _meetingCommentRepository.GetByIdAsync(new MeetingCommentId(command.InReplyToCommentId));
            if (meetingComment == null)
            {
                throw new InvalidCommandException(["To create reply the comment must exist."]);
            }

            var meeting = await _meetingRepository.GetByIdAsync(meetingComment.GetMeetingId());

            var meetingGroup = await _meetingGroupRepository.GetByIdAsync(meeting.GetMeetingGroupId());

            var meetingCommentingConfiguration = await _meetingCommentingConfigurationRepository.GetByMeetingIdAsync(meetingComment.GetMeetingId());

            var replyToComment = meetingComment.Reply(_memberContext.MemberId, command.Reply, meetingGroup, meetingCommentingConfiguration);
            await _meetingCommentRepository.AddAsync(replyToComment);

            return replyToComment.Id.Value;
        }
    }
}

View on GitHub (pinned to 91c8ef24b4)

Solutions

  1. Verify InReplyToCommentId exists and is still available before allowing reply.
  2. Refresh the thread on the client so deleted parents are removed.
  3. Pre-check parent existence in the controller and return 404.
  4. Catch InvalidCommandException at the API boundary and map to 400/404.

Example fix

// before
var replyId = await _commandDispatcher.SendAsync(new AddReplyToMeetingCommentCommand(parentCommentId, text));

// after
var parent = await _meetingQueries.GetCommentAsync(parentCommentId);
if (parent is null) return NotFound("Parent comment not found.");
var replyId = await _commandDispatcher.SendAsync(new AddReplyToMeetingCommentCommand(parentCommentId, text));
Defensive patterns

Strategy: validation

Validate before calling

var parent = await _meetingQueries.GetCommentAsync(parentCommentId);
if (parent is null) return NotFound("Parent comment not found.");
var replyId = await _commandDispatcher.SendAsync(new AddReplyToMeetingCommentCommand(parentCommentId, text));

Type guard

public static bool IsValidReplyCommand(AddReplyToMeetingCommentCommand c) =>
    c.InReplyToCommentId != Guid.Empty && !string.IsNullOrWhiteSpace(c.Reply);

Try / catch

try { await _commandDispatcher.SendAsync(cmd); }
catch (InvalidCommandException ex) when (ex.Errors.Any(m => m.Contains("must exist")))
{ return NotFound(new { errors = ex.Errors }); }

Prevention

When it happens

Trigger: Dispatching AddReplyToMeetingCommentCommand with an InReplyToCommentId that has no comment: deleted parent, wrong id, or passing a reply id whose own thread was removed.

Common situations: Threaded UI renders a cached parent that was since deleted; client posts a reply to a comment id pulled from a stale notification.

Related errors


AI-assisted analysis of kgrzybek/modular-monolith-with-ddd@91c8ef24b4 (2026-08-13). Data as JSON: /api/errors/c7ea118bb1c625d6. Report an issue: GitHub.