Linux Error Guide: 'No such file or directory' — Trace Missing Paths with strace
Diagnose ENOENT syscall failures on Linux with strace, ltrace, realpath, and readlink to find missing files, broken symlinks, and bad interpreters.
- #linux
- #troubleshooting
- #errors
- #debugging
Stuck on this Linux Admins error? Get the free incident triage checklist
A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.
Overview
ENOENT (errno 2) is the kernel’s response whenever a syscall cannot resolve one or more components of a path. Every file access goes through a kernel call — openat, stat, execve, readlink, access — and any of them returns ENOENT when the final path component or an intermediate directory does not exist.
In raw strace output the error looks like this:
openat(AT_FDCWD, "/etc/app/config.yaml", O_RDONLY) = -1 ENOENT (No such file or directory)
In shell output, application logs, and perror(3) messages it appears as the human-readable string:
No such file or directory
The causes range from a simple mistyped filename to a missing ELF interpreter inside a container, so a systematic diagnostic approach is faster than guessing.
Symptoms
- A process exits non-zero and prints
No such file or directoryto stderr. straceshows= -1 ENOENTonopenat,stat64,lstat, orexecvelines.execvereturns ENOENT even though the binary exists on disk — the ELF interpreter or a required shared library path is missing.- Bash reports
bash: /path/to/script: No such file or directoryor/usr/bin/env: 'python3': No such file or directorywhen the shebang interpreter is absent. - A Docker container or chroot exits immediately with a file-not-found message for a path that exists on the host but is not inside the confined filesystem.
lson a symlink path returnsNo such file or directoryeven thoughls -lshows the symlink itself — a dangling symlink.- A service managed by systemd fails at start with
ExecStart ... No such file or directory, often because theWorkingDirectoryis wrong or the binary path contains an environment variable that is not expanded.
Common Root Causes
File genuinely absent — the file was never created, was deleted, or lives on a host or volume that is not mounted.
Wrong working directory — a relative path like config.yaml resolves against the process CWD. If a service unit omits WorkingDirectory=, CWD defaults to / and relative paths fail.
Path typo or case mismatch — Linux filesystems are case-sensitive; Config.yaml and config.yaml are different inodes. A trailing newline embedded in a shell variable also causes this.
Broken symlink — the symlink exists but points to a target that has been deleted, moved, or renamed. Package upgrades and directory restructuring are common triggers.
Missing ELF interpreter — a compiled binary’s .interp section references /lib64/ld-linux-x86-64.so.2 (on x86-64 glibc systems) or /lib/ld-musl-x86_64.so.1 (on Alpine/musl). If that path is absent, the kernel returns ENOENT for the binary itself, not the interpreter, making the error look like the binary is missing.
Missing shebang interpreter — a script’s first line (#!/usr/bin/python3, #!/usr/bin/env node) points to an interpreter that is not installed on the current system.
Chroot or container missing files — a bind mount was not configured, a required layer was not included in the image, or a path is only meaningful outside the namespace.
$PATH lookup failure — running a command by bare name when the directory containing the executable is not in $PATH.
Diagnostic Workflow
Start with a direct check of the reported path, resolving every symlink in the chain:
ls -la /etc/app/config.yaml
realpath /etc/app/config.yaml
If the path involves a symlink, inspect the full chain:
ls -l /etc/app/config.yaml # shows -> target
readlink -f /etc/app/config.yaml # resolves all intermediate links
readlink -f returns nothing and exits non-zero for a dangling symlink.
When you cannot immediately tell which path is being opened, attach strace to the failing process and filter for ENOENT:
strace -f -e trace=openat,stat,execve -s 256 myapp 2>&1 | grep ENOENT
-f follows forks and threads. -s 256 prevents path truncation. The output pinpoints the exact path the kernel rejected.
For a command not found by name, check $PATH and binary location:
echo "$PATH"
command -v myapp
which myapp
When a binary exists but execve still returns ENOENT, it almost always means the ELF interpreter is missing. Confirm with:
file /usr/local/bin/myapp
readelf -l /usr/local/bin/myapp | grep -A2 INTERP
ls -la /lib64/ld-linux-x86-64.so.2
If the interpreter path shown by readelf does not exist, install the appropriate glibc or musl package, or use patchelf to update the interpreter path.
For shared-library resolution failures (closely related and often confused with ENOENT):
ldd /usr/local/bin/myapp
Any not found line identifies a library that must be installed or added to /etc/ld.so.conf.d/ followed by ldconfig.
Use ltrace to see library-level calls that wrap syscalls when application source is unavailable:
ltrace -e fopen,open myapp 2>&1 | head -60
Inside a running container, trace file opens from the host:
pid=$(docker inspect --format '{{.State.Pid}}' mycontainer)
nsenter -t "$pid" -m -- strace -f -e trace=openat,stat -s 256 /proc/$pid/exe
Example Root Cause Analysis
Scenario: A Go service deployed into a Docker container exits immediately with open /etc/app/config.yaml: no such file or directory. The container image was built successfully.
Confirm what is actually inside the image:
docker run --rm --entrypoint sh myimage -c 'ls -la /etc/app/'
ls: cannot access '/etc/app/': No such file or directory
The directory itself is absent. The Dockerfile COPY instruction is at fault. Checking the build context:
ls config/
config.yaml
The file lives under a config/ subdirectory, but the Dockerfile reads:
COPY config.yaml /etc/app/config.yaml
Docker’s build context does not find config.yaml at the root; because COPY of a non-existent source is silently ignored in some older BuildKit versions, the image builds but the file is never included. Fix:
# In the Dockerfile:
COPY config/config.yaml /etc/app/config.yaml
Before rebuilding, verify with a bind mount:
docker run --rm \
-v "$(pwd)/config/config.yaml:/etc/app/config.yaml:ro" \
myimage
The service starts. The root cause was a path mismatch between the Dockerfile COPY source and the actual build context layout.
Prevention Best Practices
- Use absolute paths in systemd unit
ExecStart=lines and cron job commands. Never rely on implicit CWD. - Add early existence guards in shell scripts:
[[ -f "$CONFIG" ]] || { echo "ERROR: missing $CONFIG" >&2; exit 1; }. - Validate symlinks in CI pipelines with
test -e "$(readlink -f /path/to/link)"to catch breakage before deployment. - Run
lddandreadelf -lagainst new binaries in CI as part of image validation to detect missing interpreters before production. - In Dockerfile
RUNlayers or entrypoint smoke-test stages,lsorcatrequired config paths so a missing file fails the build or startup, not the first real request. - When building minimal or scratch-based images, always include the dynamic linker package (
libc6on Debian/Ubuntu,muslon Alpine, or the relevant glibc RPM on RHEL/Rocky). - Log the resolved absolute path on file-open failure so operators can tell immediately whether the file is missing or the path is wrong.
Quick Command Reference
# Inspect a path including all symlinks
ls -la /path/to/file
readlink -f /path/to/file
# Trace every file open attempt (follow forks, full path strings)
strace -f -e trace=openat,stat,execve -s 256 myapp 2>&1 | grep ENOENT
# Find the ELF interpreter a binary requires
readelf -l /usr/local/bin/myapp | grep INTERP
# Resolve dynamic library dependencies
ldd /usr/local/bin/myapp
# Locate a command on PATH
command -v myapp
# Confirm a path exists inside a container image
docker run --rm --entrypoint sh myimage -c 'ls -la /etc/app/'
# strace inside a running container namespace (from host)
pid=$(docker inspect --format '{{.State.Pid}}' mycontainer)
nsenter -t "$pid" -m -- strace -f -e trace=openat -s 256 cat /dev/null
# Check working directory of a running process
ls -la /proc/<pid>/cwd
Conclusion
ENOENT is one of the most frequent Linux errors, and its root cause spans a wide spectrum: a typo, a missing package, a dangling symlink, or an absent ELF interpreter. The fastest diagnosis path is strace -f -e trace=openat,stat,execve to capture the exact rejected path, followed by readlink -f to unwrap symlinks, readelf -l to check the ELF interpreter, and ldd to catch missing shared libraries. Enforcing absolute paths in service files, validating symlinks in CI, and checking binary interpreter paths during image builds eliminates most production occurrences before they reach users.
Fixed it? Get 500 Linux Admins & DevOps AI prompts — free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.
Did this fix your issue?
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.