Description
Symfony version(s) affected: 5.1.7
Description
The issue stems from a failed Messenger message. I have a failed Doctrine transport configured. Messages, together with a serialized trace are being saved. However, when attempting to print the error via ./bin/console messenger:failed:show 6 -vv
, the command fails with an error in ErrorHandler's FlattenException class:
In FlattenException.php line 360:
[TypeError]
Return value of Symfony\Component\ErrorHandler\Exception\FlattenException::getTraceAsString() must be of the type string, null returned
I did manually decompose the data saved in the headers
field in messenger_messages
and it seems that X-Message-Stamp-Symfony\Component\Messenger\Stamp\RedeliveryStamp
contains a flattenException
field/object which only has the following fields: type
, title
, status
, detail
, class
, trace
.
Judging by FlattenException::$traceAsString
's usages, I don't see how it's empty, but I do see how it can be skipped when persisting to the database, and deserialization will afterwards not work.
Setting getTraceAsString
's return type to ?string
fixes that problem, but then
In SymfonyStyle.php line 445:
[TypeError]
Argument 1 passed to Symfony\Component\Console\Style\SymfonyStyle::writeBuffer() must be of the type string, null given, called in /home/wucdbm/PhpstormProjects/gems-api/vendor/symfony/console/Style/SymfonyStyle
.php on line 376
Casting FlattenException::$traceAsString
to string works OK, but the exception trace in the command output is naturally missing.
How to reproduce
- install Symfony + messenger component
- configure
failure_transport: failed
underframework.messenger
- configure
failed: 'doctrine://default?queue_name=failed'
transport underframework.messenger.transports
- configure any other transport and route below Message to it
- create a Message
- create a handler for the Message, in my case a class implementing
__invoke()
, immediately throw an Exception and make it fail - Update schema to create the
messenger_messages
table - send a Message
- run
bin/console messenger:failed:show
- run
bin/console messenger:failed:show <id> -vvv
Possible Solution
Possible solutions (worst to best, IMO)
- cast $traceAsString to string (null -> '') (to my knowledge, $traceAsString can't be null in any other case but deserialization?)
- save traceAsString in
messenger_messages
in addition to the currently persisted data - rebuild traceAsString from the trace array upon deserialization
I guess the correct solution to this problem would be rebuilding $traceAsString
from the trace array as storing large text values in DBs isn't the greatest idea.
Besides, for an app that has a failed messages explorer, the trace array is sufficient to have a custom-formatted UI. I guess traceAsString only matters in CLI, so no big deal if we're rebuilding traceAsString every time we have to print it in the terminal.
Unfortunately, I have only recently started to actively use the Messenger component and I am not that familiar with its internals. On top of that, I am currently chasing strict deadlines and can't spend much time on this, at least for the foreseeable future.
Additional context
- show messages.png
- show message verbose.png
- show message return type null.png
- show message case null to empty string.png