Linux / shell
No such file or directory — when the missing file is the shebang interpreter
Written and reviewed by Sahil Srivastav
bash: ./deploy.sh: /bin/bash^M: bad interpreter: No such file or directoryWhat this error actually means
A shell script can exist at exactly the path you executed and still fail with a missing-file error. Direct execution asks the kernel to load the interpreter named on the first line. With Windows CRLF line endings, the carriage-return byte can become part of that interpreter name: /bin/bash followed by byte 0d is a different path from /bin/bash.
Older Bash diagnostics often display that carriage return as ^M, as in the example. Other shells or versions phrase the failure differently. With #!/usr/bin/env bash, env may start successfully and then fail to find a command whose name includes the carriage return. The underlying clue is the first line’s bytes, not one exact wording.
There is another lookalike: a native ELF executable may request a dynamic loader that is absent from a minimal image. That also produces a missing-file failure even though the executable exists. Establish that the file is a script before treating every such report as a line-ending problem.
Causes, most common first
- 1CRLF was introduced into the executable script. An editor, checkout configuration or archive-generation step converted line endings. The visible first line looks correct because many editors hide carriage returns. The deployed bytes, rather than the repository’s web view, are the evidence that matters.
- 2The interpreter genuinely is missing in the target environment. A script may use /bin/bash in an image that contains only another shell. The host’s /bin/bash is irrelevant inside that image. Replacing the shebang with /bin/sh is valid only after checking that the script does not rely on Bash syntax.
- 3An env shebang selects an unavailable runtime. After line endings are ruled out, #!/usr/bin/env python3 still depends on the target environment’s PATH. A virtual environment activated on the developer’s machine is not embedded in the script or automatically activated by the service launcher.
- 4A binary needs a loader that the image does not contain. A glibc-linked executable copied into an incompatible minimal image can exist and have execute permission while its requested loader is absent. Converting its bytes as though it were a text file corrupts the executable and cannot fix the dependency.
When you see it
- A script starts failing after editing or packaging on a Windows machine
- The interpreter path is present, but the error shows ^M or an escaped carriage return
- The same source works from one checkout and fails from the release artifact
- Explicit interpreter invocation changes the error but may reveal more carriage-return problems in the body
How to diagnose it
Step 1
Inspect the first line as bytes
A shebang ending in 0d 0a has CRLF; 0a alone is a Unix newline. od works even when the terminal renders the carriage return invisibly. The file command adds a useful classification, but the byte check is decisive.
head -n 1 ./deploy.sh | od -An -tx1
file ./deploy.shStep 2
Make control characters visible
GNU sed’s l command escapes nonprinting characters and marks the end of the line. A visible \r before the end marker confirms the extra carriage return. Do this against the artifact on the failing machine, not a separately checked-out copy.
sed -n '1l' ./deploy.shStep 3
Check the intended interpreter in the same image
For the /bin/bash example, verify both existence and executable mode. If you use env, also run command -v for the requested runtime under the launcher’s PATH. Correct line endings do not install a missing interpreter.
ls -l /bin/bash
/bin/bash --versionStep 4
If file reports ELF, inspect its requested loader
Use readelf from binutils for a native executable named app. Find the program interpreter in the output, then check that path inside the target image. This is a separate diagnosis; do not run text line-ending tools on the binary.
readelf -l ./appThe fix
Convert the affected text script to LF in the source tree, review the diff and rebuild the release. dos2unix is suitable when available. The Python example below replaces CRLF pairs only and preserves the existing file’s mode because it writes back to the same path. It assumes the target has already been identified as the intended text script.
Normalise the whole script, not just the shebang. A carriage return on a later line can become part of an argument or command name, so repairing only the first line may merely move the failure further into deployment. Test the corrected artifact from the real launcher after rebuilding.
Prevent recurrence with a repository attribute such as *.sh text eol=lf and editor configuration that agrees with it. Review any renormalisation separately because it can touch many files. If the interpreter or ELF loader is missing, build against the target runtime or include the required runtime dependency instead of treating that case as CRLF.
python3 - <<'PY'
from pathlib import Path
p = Path('deploy.sh')
data = p.read_bytes()
p.write_bytes(data.replace(b'\r\n', b'\n'))
PY
head -n 1 ./deploy.sh | od -An -tx1How to stop it coming back
- Validate executable scripts from the final archive or container so packaging transformations are covered.
- Keep line-ending policy explicit for shell files and run a small execution smoke check on Linux.
- Record the interpreter as a runtime dependency; line-ending checks cannot catch a base-image change that removes Bash.
FAQ
Will chmod +x fix this?
No. Execute permission and interpreter lookup are separate checks. An executable file can still name a nonexistent interpreter containing a carriage return. Inspect the bytes before changing permissions.
Why does bash deploy.sh get further?
It bypasses the kernel’s shebang interpreter lookup by explicitly choosing Bash. Carriage returns elsewhere remain in the input and can still change parsing or arguments. It is an isolation clue, not a complete line-ending repair.
Should I make a symlink with ^M in its name?
No. That preserves a malformed artifact and depends on an accidental interpreter path. Correct the source and packaging process so every deployment receives the intended bytes.
Related
Other errors engineers hit next to this one
- awaitTermination never returns and the JVM will not exit
- InterruptedException caught and ignored — the task can no longer be cancelled
- Two unrelated components sharing a monitor via a boxed Integer or interned String
- Cache stampede — the same expensive value built many times concurrently
- Lock convoy — throughput collapses as threads are added, with no deadlock
- ReadWriteLock writer blocked indefinitely behind a stream of readers
- Worker loop never sees the stop flag and runs forever
- psycopg2.InterfaceError: connection already closed