After investigation I've found that this is because RedirectResponse does not terminate the current script unlike drupal_goto, so the page is still rendered and messages get output but then immediately lost. A quick fix for this is to insert a call to exit() after sending the response.