ErrLookupBackground articles › "File not found" and ENOENT errors: why libraries can't find a file that should exist

"File not found" and ENOENT errors: why libraries can't find a file that should exist

"File not found" errors — ENOENT in Node, FileNotFoundException in Java, PHP "not found" exceptions, and 404 aborts — fire when a library's resolved path does not exist on disk. A developer meets this family when a CLI argument points nowhere, a template or asset name breaks the naming contract, a relative path resolves from an unexpected working directory, or a file was deleted after something started referencing it. This article covers the shared mechanics across the family's documented records: how path resolution, sanitization, case sensitivity, symlinks, and stale references produce these errors, and which fixes generalize across libraries.

Distilled from 110 documented records across 50 repositories.

Background

The family sits at the boundary between a path a library computed and the filesystem it asked for. Nearly every record has the same shape: something resolves a name to a concrete path — a CLI validator expanding an @file argument (spree), a template resolver gluing prefix, name, and format together (career-ops), a config plugin reading a service-discovery file at configure time (fluentd), a native loader opening input before parsing (swc's parseFileSync and transform), or a storage layer emulating GridFS over Postgres rows (ruflo) — and the existence check fails. What varies is when the check runs: fluentd reads the file at configure time because it builds the initial server list then, Puppet's init service provider resolves the script lazily at first command execution, and claude-mem checks mode files at load time. Some libraries check one path; others walk a candidate list and only fail when every candidate misses, as claude-mem's mode directories and claude-mem's MCP server script resolver do.

From the caller's side the message almost always names the expected path or name, which is the family's most useful property: career-ops prints the exact filename its template resolution landed on, claude-mem lists every directory it searched, and caveman echoes the offending input. But severity is library-specific: some throw and abort before any work starts (wallabag's import command), some downgrade to a warning with a fallback — claude-mem falls back to the built-in 'code' mode, and RocketChat removes the app record while only warning about the missing package file — and some defer the failure into another layer entirely. In native bridges the file-load failure can surface as a panic-converted JavaScript exception whose text does not name the file at all (the swc records carry unrelated decorator and duplicated-methods message text), so the missing-file cause is easy to miss unless you know the string overload means a path.

The recurring traps are structural. Relative paths resolve against whichever working directory the process happens to have — different between web and CLI PHP runtimes, between rspec, rake, spring, and IDE runners, and between write time and read time (ruflo sealed trajectories silently redirect if cwd changes) — while career-ops resolves photo paths against the script's own directory rather than the caller's cwd. Sanitizers can change the lookup key before the check: bagisto and anything-llm strip traversal-shaped segments so the searched name differs from the URL name, and career-ops kebab-cases template names before building the filename. Filesystem features add more: a missing public/storage symlink silently removes a lookup base in bagisto, dangling symlinks read as nonexistent in gradle because File.exists() follows links, and deno's embedded VFS is case-sensitive on Linux targets where the host filesystem a developer tested on may not be.

A distinct sub-family treats "file" as a record rather than a disk path. ruflo's Postgres backend throws "File not found" when the files-table row is gone while conversation metadata still references the id; RocketChat's GridFS-backed app packages hit "File not found for id" when another cluster instance wins the delete race during an uninstall; bagisto's download controller returns 404 when the private-disk payload behind an already-paid purchased link was deleted. These are referential-integrity failures that look identical to filesystem misses, and their fixes — cleaning dangling references, reconciling records, re-capturing artifacts — differ from simply creating the file.

Common causes

What usually fixes it

Go deeper

Documented occurrences

…and 90 more across the corpus — use search.

Honest provenance: generated on 2026-08-22 from AI-assisted analysis of the linked records. See how records are made.