Forced rendered node view results in empty node links
Use renderPlain():

$message['body'][] = \Drupal::service('renderer')-&gt;renderPlain($body_data);


From the documentation:


  Renders final HTML in situations where no assets are needed.
  
  Calls ::render() in such a way that placeholders are replaced.
  
  Useful for instance when rendering the values of tokens or emails,
  which need a render array being turned into a string, but do not need
  any of the bubbleable metadata (the attached assets and cache tags).


Source: Renderer::renderPlain



"When I render the same content into an html page, node__links are correctly filled."

render() in a page works, because the page has a render context for the metadata to bubble up, so that the placeholders can be replaced later.