After a couple hours of debugging, here is what I figured out:
-
The unfortunately-named
wp_trim_excerpt()
does not merely trim the excerpt passed to it, but also assembles an excerpt if a blank string is passed in. (Yes, I see now that this is in the documentation, but I was grep’ing through code trying to trace what was being hooked intoget_the_excerpt
.) -
The call to
excerpt_remove_blocks()
is the line withinwp_trim_excerpt()
which first returned a blank string. -
excerpt_remove_blocks()
goes through all the blocks in the post and filters out ones whose names are not on an approved list, called$allowed_inner_blocks
, which is stored within the function. -
Although in the editor my post appeared to be a normal post with paragraph and image blocks, all those blocks were secretly part of one big group block with the name
core/group
, which is not part of$allowed_inner_blocks
. Thus, whenexcerpt_remove_blocks()
looped over the blocks, it found one block of an unacceptable type and returned nothing. -
But how would I have known about this group block?! I could find no indication in the block editor that all these blocks were grouped. The block editor worked as it always does, and I could add/delete/move blocks as usual with no issues. I could also create new groups out of existing blocks, but I could not undo this existing group or see any indication of its presence.
-
To remove the hidden group, I had to switch into the Code Editor from the Visual Editor. There, I saw the whole text of the post appeared like so:
<!-- wp:group --> <div class="wp-block-group"> <div class="wp-block-group__inner-container"> ... </div></div> <!-- /wp:group -->
-
I deleted these lines, updated the post, and voila — now an excerpt appears as expected on the homepage of my blog.
As to how this group got in there in the first place — no earthly idea. But I hope this solution now shows up in search to save time for the next poor soul who encounters this situation.