Can you track down where you read that?
I can't remember for sure, but it was eventually here. There is a comment in the middle of his code that states this. I believe it is not a constraint that's officially documented in an API; rather, it is logical, because we can't change the GID if we are already a non-privileged user (that is, if we have already changed our EUID away from root or from the suid-user, respectively).
I agree with you that this probably is not a bug Perl, but it's a bug in the script, which, after all, is probably used by a lot of people because it claims to show how to securely drop privileges, preventing them from coding own solutions because nobody wants to have errors in security related code.
in the list-assignment case, we drop privileges (by setting a non-root EUID) before trying to set EGID; it does not seem unreasonable to me that the attempt to set EGID then fails.
This is what I initially thought, too. But as mentioned above, I have reversed the order of $EUID and $EGID in the two list context assignments at the begin and the end of the script for testing. This didn't change anything. I can't explain this, because I would have expected that list assignments go from the left to the right on both sides, and thus that the group id assignment would happen before the user id assignment after that change.
Perhaps Perl tries to be overly smart; in every case, for whatever reason, it insists on changing the user id first regardless of the variable order in the list. Anyway, I am not that deep in Perl, so I can't tell whether it guarantees a certain order of single assignments in a list assignment at all. To make it work as expected, I had to write the two assignments each on its own in scalar context in the correct order.
Sadly I cannot readily test any of this, since on my Ubuntu system the setugid bits are ignored for scripts.
In case you are interested, I describe how I test it. Actually, I was in the same situation as you: Debian bullseye (or the interpreters, respectively) also ignores those bits on scripts. Plus, most of the suid-wrappers contain bugs and don't work as expected as well.
However, I have found that one of those wrappers works like a charm: suid-wrapper. It is also available as a snap (Ubuntu can handle snaps, correct?), but downloading the source and compiling it really was a matter of two minutes, and it worked out of the box. No annoying study of man pages ... just copy the example command from the Github page and go on.
In case of a Perl script without command line parameters (like our example script), it even simpler. Just execute
sudo suid-wrapper --output root_pl test.pl
This generates the stub root_pl, which is an executable that runs test.pl. The set[u|g]id flags on the script test.pl itself are still ignored, but you can change the owner of the stub executable root_pl to whatever user and group you want and then set the suid and sgid flags as you like.
Furthermore, you can alter the actual script test.pl afterwards without generating a new stub, as long as you don't change its name of course. This is extremely nice.
As a final remark, suid-wrapper by default sets the stub it generates suid-root and setgid-root, but as mentioned above, you can change the owner and group as you like. Just remember to make such changes to the stub, not the script. And please remember that Ubuntu probably resets suid and sgid flags on the stub if you change its owner or group. At least, this happens on Debian (and has driven me mad because I didn't notice it at once). It is a safety mechanism I guess.