Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Linux Admins By James Joyner IV · · 9 min read Last reviewed Jul 2026

Linux Error Guide: 'No such file or directory' — Trace Missing Paths with strace

Quick answer

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
Free toolkit

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 directory to stderr.
  • strace shows = -1 ENOENT on openat, stat64, lstat, or execve lines.
  • execve returns 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 directory or /usr/bin/env: 'python3': No such file or directory when 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.
  • ls on a symlink path returns No such file or directory even though ls -l shows the symlink itself — a dangling symlink.
  • A service managed by systemd fails at start with ExecStart ... No such file or directory, often because the WorkingDirectory is 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 ldd and readelf -l against new binaries in CI as part of image validation to detect missing interpreters before production.
  • In Dockerfile RUN layers or entrypoint smoke-test stages, ls or cat required 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 (libc6 on Debian/Ubuntu, musl on 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.

Free download · 368-page PDF

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?

Free download · 368-page PDF

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.