Unfortunately I can't use shell=True or cwd=whatever/.., I don't want a subshell.
Neither providing the full path of the command or setting the PATH environment variable makes any difference.  Calling shutil.which() on the command returns the expected path.  I've tried providing the command as a string and as a list.
But, as mentioned, what is really killing me is that the identical code works perfectly on a slightly newer R-Pi with an identical directory structure.
(Moved solution on behalf of the question author to move it to the answer section).
The second R-Pi had no /usr/bin/bash, only /bin/bash ..
Shell files being called by Popen had #!/usr/bin/bash headers, so 'file not found' was the correct error, but it was interpreter not found, rather than script not found, as I'd assumed!
python3.7 subprocess.Popen throws "[Errno 2] No such file or directory" on one R-Pi but not the other, both running buster