Looks like stdout is also ignored with code 2, and you need to output plain text on stderr:
"Exit 2 means a blocking error. Claude Code ignores stdout and any JSON in it. Instead, stderr text is fed back to Claude as an error message."