ErrLookup › Background articles › "Config file not found": what it means and how to fix it in docker-sync, Maven, Vagrant, Turborepo and other tools
"Config file not found": what it means and how to fix it in docker-sync, Maven, Vagrant, Turborepo and other tools
"Config file not found" errors appear when a CLI, server, or library tries to load a configuration file — by explicit path, well-known default, or directory traversal — and the file does not exist or cannot be read at the resolved path. This guide covers why the path resolution surprises you (cwd-relative paths, parent-directory searches, container mounts), the common variants of the error across 60+ open-source projects, and the fixes that hold across the family.
Distilled from 97 documented records across 60 repositories.
Background
This family covers every error a tool raises when the configuration file it needs is not present at the path it resolved. The triggering layer is almost always the very first step of startup: before any parsing, validation, or business logic runs, the code does a stat, exists(), isFile(), or read call on a config path and fails. Because it fires before anything else, you get a terse, path-centric message — 'configuration file not found', 'File /path/x.json does not exist.', 'No docker-sync.yml configuration found in your path' — instead of a stack trace from deeper inside.
The key thing the records reveal is that 'the path it resolved' is rarely just the string you typed. Libraries resolve the path differently, and that resolution is where most confusion lives. Some expand relative paths against the current working directory (Maven's -t, -ps, and -is options; Turborepo's --config; linera-exporter's --config). Others expand against a project root instead of cwd: Vagrant expands chef.validation_key_path against machine.env.root_path (the Vagrantfile's directory), so a relative path that 'looks right' from your shell resolves elsewhere. Docker-sync and Hasura's GraphQL engine walk upward from the current directory through every ancestor looking for an exact filename, so the file must sit in the current directory or a parent — and the lookup is exact-name only (docker-sync.yml will not match docker-sync.yaml). Others combine markers and defaults: claude-task-master only complains about a derived root if it looks like a project (.taskmaster/ exists) but the config inside is missing, while golangci-lint's custom builder does the opposite — no parent search at all, only the exact .custom-gcl.{yml,yaml,json} in the current directory.
Whether a missing file is a hard error or a soft fallback is library-specific, and even within one library it depends on how the path was supplied. Repomix treats an explicit --config as the user's intentional choice and fails hard, but silently falls back to discovery when no flag is given. Task Master downgrades a missing config to a warning plus defaults. Tailscale's conffile.Load wraps read-phase failures in a sentinel (ErrNoConfig) so callers can distinguish 'absent' from 'read but unparseable', and its optional: config prefix makes absence acceptable. Presto treats an unreadable password file as CONFIGURATION_UNAVAILABLE — a server configuration problem, not an authentication failure — so every auth through that store fails until the file is readable. Cilium's Hubble metrics watcher keeps the last good config and retries on the next filesystem event. And Teleport's updater distinguishes an expected absence (Kubernetes upgraders skip updater info when errors.Is(err, ErrConfigNotFound)) from a real problem.
Some members of the family are not strictly 'not found' at all: Go's rbac.LoadPermissions and Cilium both wrap the raw OS error with %w, so the message tells you whether it was no-such-file, permission-denied, or is-a-directory. Oh-my-pi splits explicitly: ENOENT yields 'Config overlay not found', anything else yields 'Failed to read config overlay' with the underlying error. Reading the exact wording matters, because permission problems and wrong-cwd resolution masquerade as missing files.
Common causes
- Relative path resolved from the wrong working directory. Most tools expand a relative --config path against the process cwd, and scripts, CI steps, or service managers frequently run from a different directory than your shell. Maven resolves -t/-ps/-is against the JVM's cwd, repomix resolves against rootDir, and Vagrant expands provisioner paths against the project root — each a different anchor that surprises in a different way.
- Wrong or near-miss filename. Several lookups are exact-name only: docker-sync only matches docker-sync.yml (not .yaml or .dist), golangci-lint's custom builder only matches .custom-gcl.{yml,yaml,json} with a leading dot, and phabricator's Diviner only discovers *.book files. On case-sensitive Linux filesystems, casing differences that work on macOS also fail.
- File never created or not initialized on first use. Some tools have no implicit sample creation: claude-mem requires an explicit init (writeSampleConfig) before the watcher runs, task-master points at 'task-master models --setup', and a fresh clone that gitignored or never committed the config simply has nothing to load.
- Config not copied or mounted into the runtime environment. In containers and CI, the file exists on your machine but not in the image or job workspace. Records from fastmcp, gofr, spleeter, and Maven all call out missing COPY/volume mounts or CI steps where a file generated in an earlier stage did not persist.
- Search exhausted without finding the file. Tools that traverse upward or scan subtrees fail when nothing matches: docker-sync walks from cwd to the filesystem root, Hasura's recursivelyValidateDirectory stops at /, and Diviner finds no .book anywhere beneath getcwd(). The error means 'looked everywhere it knows and found nothing', not that one specific path was wrong.
- Readability, not absence: permissions, directories, and mounts. Go's RBAC loader, Presto's password file, Presto's router scheduler config, and oh-my-pi all fail when the path exists but is unreadable — permission denied, the path is a directory, a broken symlink, or an unmounted volume. The message often wraps the raw OS error, so check whether it really says ENOENT.
- Boot and startup races. Kubernetes ConfigMap mounts can briefly disappear during kubelet refresh (Cilium), and tailscaled can start before its config file is written. The file exists eventually but not at the moment of the read.
What usually fixes it
- Confirm the resolved path before anything else: the error message usually prints the path after resolution, so ls/test -f that exact path from the same user and working directory the process runs in. Printing os.getcwd() when debugging is the single fastest way to find wrong-cwd resolution.
- Use absolute paths in scripts, CI, systemd units, and container entrypoints — this recurs across Maven, repomix, deepagents, linera-exporter, sglang, and oh-my-pi as the standard prevention. Build them from a known anchor (path.resolve, Path(__file__).parent) rather than assuming where the process starts.
- Match the library's discovery rules exactly: know whether your tool searches parent directories (docker-sync, Hasura: yes; golangci-lint custom builder, Turborepo with --config: no), what anchor relative paths use (cwd vs project root), and the exact accepted filenames and extensions.
- Make the file present in the runtime environment, not just on your dev machine: commit it (unless it holds secrets, which belong in gitignored files), COPY or mount it into containers, and generate it in the same CI step that consumes it — or add a test -f / test -r preflight check in wrapper scripts.
- Distinguish 'missing' from 'unreadable' in code: several libraries expose sentinels or wrapped OS errors for exactly this (tailscale's errors.Is(err, conffile.ErrNoConfig), Teleport's ErrConfigNotFound, Go %w-wrapped read errors). Branch on the sentinel to tolerate absence and fall back to defaults, and treat parse errors separately.
- For startup races, order the dependencies: systemd ConditionPathExists=/After=, initContainer waits or readiness checks before the consumer starts, and rely on watchers that retain the last good config across transient mount windows where the library provides one.
Documented occurrences
- No docker-sync.yml configuration found in your path ( traversing up ) Did you define it for your project? (EugenMayer/docker-sync)
- updater config file not found (gravitational/teleport)
- configuration file not found (golangci/golangci-lint)
- Configuration file {descriptor} not found (deezer/spleeter)
- Config file not found at ${argConfigPath} (yamadashy/repomix)
- The validation key set for `config.chef.validation_key_path` does not exist! This file needs to exist so it can be uploaded to the virtual machine. (hashicorp/vagrant)
- Unable to read the configuration file (linera-io/linera-protocol)
- Configuration file not found: {file_path} (PrefectHQ/fastmcp)
- Group "${groupName}" not found (abhigyanpatwari/GitNexus)
- The specified installation settings file does not exist: {} (apache/maven)
- Profile configuration file not found at: ${customProfilePath} (santifer/career-ops)
- ErrNoConfig: %w: %v (tailscale/tailscale)
- `%{config_option}` does not exist on the %{system}: %{path} (hashicorp/vagrant)
- Cannot locate openclaw.json — please add hooks.allowConversationAccess manually (TencentCloud/TencentDB-Agent-Memory)
- The specified user toolchains file does not exist: {} (apache/maven)
- Warning: Configuration file not found at provided project root (${explicitRoot}). Using default configuration. Run 'task-master models --setup' to configure. (eyaltoledano/claude-task-master)
- The specified project settings file does not exist: {} (apache/maven)
- File {normalized_input} does not exist. (sgl-project/sglang)
- The specified installation toolchains file does not exist: {} (apache/maven)
- MCP config file not found: {mcp_config_path} (langchain-ai/deepagents)
…and 77 more across the corpus — use search.
Honest provenance: generated on 2026-09-04 from AI-assisted analysis of the linked records. See how records are made.