“I downloaded wp-cli.phar, uploaded it to ~/bin/wp, SSH’d in, and ran wp. Got -bash: wp: command not found. The file is right there. Why?”
If you’ve set up WP-CLI on a shared host using a browser-based file manager, you may have hit exactly this. The file exists, permissions are 755, size is 6.8 MB (matches the official PHAR). And yet it refuses to run. The “command not found” message can hide an entirely different problem underneath.
The file is there, but nothing runs
After SSHing in, you can confirm the file exists:
$ ls -la ~/bin/wp
-rwxr-xr-x 1 c1234567 c1234567 7142777 May 8 10:00 /home/c1234567/bin/wp
$ wp --info
-bash: wp: command not found
You might suspect ~/bin isn’t on PATH (the environment variable that lets you run a command by name alone). But calling it by absolute path gives a different, more telling error:
$ ~/bin/wp --info
-bash: /home/c1234567/bin/wp: cannot execute binary file
“Cannot execute binary file” — at this point, something is wrong with the file itself, not just the shell setup.
The real cause — a PHAR broken signature
WP-CLI ships as a single PHAR file (wp-cli.phar) — a PHP archive format that bundles all of WP-CLI’s code into one self-contained executable. PHARs carry a SHA1 signature inside, and PHP verifies that signature when loading the archive. If the bytes don’t match what was signed at build time, loading is refused.
Running wp through php directly surfaces the error that was hidden until now:
$ php ~/bin/wp --info
Fatal error: Uncaught PharException: phar "/home/c1234567/bin/wp"
SHA1 signature could not be verified: broken signature in
/home/c1234567/bin/wp:3
That’s the smoking gun. If a single byte of the PHAR has been altered, the archive refuses to load. Which means the PHAR that arrived on the server is not the same one that left the build server — something between download and upload changed it.
Why the browser file manager corrupts it
The typical workflow looked like this:
- Download
wp-cli.pharfrom the WP-CLI website on a Mac - Rename it to
wpin Finder (drop the.pharextension) - Use the host’s browser-based file manager to drag-and-drop the file into
~/bin/
Step 3 is the trap. In the file manager, a file named wp (with no extension) is shown as a text file icon, and the file properties dialog labels it “Plain Text.” Without an extension, MIME detection (the mechanism a file manager uses to guess what kind of file it is) falls back to a text guess, and the upload path applies subtle byte transformations along the way — line-ending conversion, character-encoding normalization, or similar — which corrupt the binary.
We tested the same procedure across different accounts and time windows. Each upload produced a different MD5 hash (a short fingerprint string used to confirm two files have identical contents) of corruption. It’s not deterministic damage; it’s some kind of transformation happening in flight.
The fix — never go through the Mac
The reliable path is to SSH into the server first, then download with curl directly on the server. Neither Finder nor the file manager touches the file.
ssh -i ~/.ssh/<your-private-key.pem> -p 8022 <ssh-user>@<host> '
mkdir -p ~/bin && cd ~/bin &&
[ -f wp ] && mv wp wp.broken_backup_$(date +%s);
curl -sO https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar &&
mv wp-cli.phar wp && chmod 755 wp &&
~/bin/wp --info
'
This idempotent one-liner moves any existing wp to a timestamped backup before pulling a fresh PHAR. The final wp --info is the proof that it works. The whole thing takes about 30 seconds.
Takeaway — binaries go through SSH/SFTP only
Browser file managers are convenient, but they treat unfamiliar files as text by default, and transformations along the upload path can quietly corrupt binary content. WP-CLI is fortunate: the PHAR’s SHA1 self-check means corruption fails loud and early. Binaries without internal integrity checks (other executables, archives, image metadata) could carry the same corruption silently into production.
There’s a corollary: hash comparison alone is not enough to declare a PHAR healthy. We observed cases where two PHARs share an identical MD5 but one still fails with broken signature on first execute. The final “this works” verdict has to come from wp --info‘s exit status (the number a command returns to indicate success or failure — 0 means success) and its output — not from a hash.
For the broader picture of how different shared hosts ship (or don’t ship) WP-CLI, see also a four-host investigation of WP-CLI architectures.