guides / exec-format-error troubleshootingupdated 2026-10-25 readelf -h ./app

cannot execute binary file: Exec format error — causes and fixes

2026-10-25 By luckyray Debian 12 · Ubuntu 24.04 · Alpine 3.20

You try to run something and the shell refuses:

$ ./agent
bash: ./agent: cannot execute binary file: Exec format error

The instinct is to assume the file is corrupt. It almost never is. Exec format error (ENOEXEC) means the kernel looked at the first bytes, could not find a format handler that accepted them, and gave up. The file is intact — it is simply not something this kernel can run.

The quick diagnosis

Three commands settle it in about ten seconds:

$ file ./agent
./agent: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked,
         interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 3.7.0, stripped

$ uname -m
x86_64

$ readelf -h ./agent | grep -E 'Class|Machine'
  Class:                             ELF64
  Machine:                           AArch64

file says aarch64, uname -m says x86_64. There is your answer: an ARM build on an Intel host. No amount of chmod +x will change it.

What the kernel actually does

execve(2) never looks at the file extension. It reads the first 128 bytes and walks a list of binfmt handlers, each of which gets a chance to claim the file. The ELF handler claims it when bytes 0–3 are 0x7f 'E' 'L' 'F', then validates two more fields before anything else happens:

FieldMeaningRejection
EI_CLASS1 = 32-bit, 2 = 64-bitvalue outside 1–2 is rejected
EI_DATA1 = little-endian, 2 = big-endianinvalid value is rejected
e_machinetarget instruction setmust match a machine the kernel was built for

If any of these fail, execve returns ENOEXEC and the dynamic loader is never invoked. This single fact rules out an entire class of theories: when you see Exec format error, the problem is never a missing library, a bad LD_LIBRARY_PATH, or a permissions issue. Those all produce different messages, because they happen after the kernel has accepted the file.

A useful side effect of ENOEXEC: POSIX shells respond to it by re-executing the file through the shell itself. That is why a text file with no shebang sometimes produces shell syntax errors instead of an exec error — and why bash ./script.sh can “work” where ./script.sh does not.

The five real causes, in order of frequency

1. Architecture mismatch

By far the most common, and now the most common of the common: an arm64 image or binary on an amd64 host, or the reverse. Apple Silicon made this routine — a laptop builds arm64, the CI runner or the server is x86_64.

For containers the check is:

$ docker image inspect myapp:latest --format '{{.Architecture}}'
arm64

$ uname -m
x86_64

Fix it by asking for the right platform explicitly, rather than relying on whatever the builder defaulted to:

docker pull --platform linux/amd64 myapp:latest
docker buildx build --platform linux/amd64 -t myapp:amd64 .

Emulation (qemu-user-static registered through binfmt_misc) makes the wrong binary run, which is worse than failing: it works, slowly and subtly differently, and nobody notices until production. Docker Desktop enables this transparently, which is exactly how it ends up in production.

2. A shell script with Windows line endings or a BOM

$ ./deploy.sh
bash: ./deploy.sh: cannot execute binary file: Exec format error
$ file deploy.sh
deploy.sh: Bourne-Again shell script, ASCII text executable, with CRLF line terminators

The shebang line becomes #!/bin/bash\r, the kernel looks for an interpreter literally named bash\r, does not find it, and returns ENOEXEC. A UTF-8 BOM (EF BB BF) in front of #! breaks it the same way — the first two bytes are no longer #!.

dos2unix deploy.sh          # or: sed -i 's/\r$//' deploy.sh
sed -i '1s/^\xEF\xBB\xBF//' deploy.sh   # strip BOM

3. A truncated or replaced download

An HTML error page saved under the name agent, or a tarball that was actually a 404 body, fails at the very first byte check — sometimes as Exec format error, sometimes as Syntax error when the shell retries. Verify size and checksum before blaming the platform:

$ head -c 40 ./agent | od -c | head -2
0000000   <   !   D   O   C   T   Y   P   E       h   t   m   l   >

That is an HTML page, not an ELF header. 0x7f E L F is what you want to see.

4. A 32-bit binary on a host without multilib

An i386 executable on a 64-bit kernel needs CONFIG_IA32_EMULATION and a 32-bit loader (/lib/ld-linux.so.2) plus 32-bit libc. Without them the kernel can still refuse at exec time:

sudo apt install libc6-i386        # Debian/Ubuntu
sudo dnf install glibc.i686        # RHEL family

If readelf -h reports ELF32 and Machine: Intel 80386 while the target is a minimal container image, installing multilib is usually the wrong fix; rebuilding for the target is the right one.

5. Shebang pointing at a missing interpreter

$ ./tool
bash: ./tool: /usr/bin/env/python3: bad interpreter: No such file or directory

The message names the interpreter, not the format, but the same ENOEXEC path is involved. Note the difference from cannot execute binary file — the kernel did recognise the script, and failed to find what it points at.

When it is none of the above

Two rarer cases are worth knowing:

  • Filesystem or mount problem. A binary on a noexec mount produces Permission denied, not Exec format error, so this is usually easy to rule out. A binary served from an NFS mount with the wrong arch option is a different story.
  • Kernel architecture mismatch (not userland). A 32-bit kernel with a 64-bit userland cannot run 64-bit binaries at all. uname -m shows the kernel’s view; check getconf LONG_BIT for the userland width. If they disagree, nothing you install will help.

A note on the tools

file reads the ELF header and reports architecture, class and endianness in one line. readelf -h gives you the same fields individually, which is what you want when scripting a check. If you cannot install either — a locked-down jump host, a container with no binutils, a binary you would rather not copy onto a server — the ELF Inspector reads the same structures in a browser tab and reports e_machine, class and endianness without any upload.

Related: wrong ELF class covers the 32/64 mismatch that only shows up at link time, and GLIBC version not found covers the failure that happens immediately after this one is solved.


Every command in this guide was run against the distribution named in the output. If your output differs, the mechanism section explains why — and the ELF Inspector shows the same fields without needing a shell.

Related

guides
  1. version `GLIBC_2.34' not found — why copying libc never works How glibc symbol versioning works, why a binary built on a newer distribution refuses to start on an older one, and the four legitimate fixes.
  2. wrong ELF class: ELFCLASS32 or ELFCLASS64 — what it means The 32-bit versus 64-bit mismatch that shows up at link time and at dlopen time, why it is not the same as Exec format error, and how to resolve it.
  3. error while loading shared libraries: cannot open shared object file How the dynamic loader searches for libraries, why a library can be installed and still not found, and the five fixes that work in order of preference.