at the top of the main loop, have_posts() and is_404() are mutually exclusive.
That is true sometimes, but not always. More specifically, it can be false when a non-permalink URL seeks a non-existent post. For example,
Issue a 404 if a permalink request doesn’t match any posts. Don’t issue a 404 if one was already issued, if the request was a search, or if the request was a regular query string request rather than a permalink request.
Now it makes sense that the single.php template file would check
have_posts(). Let the healing begin.
(Are there any documented examples of this bug occurring outside of single.php?)