caddyserver/caddy · error
determining current executable path: %v
Error message
determining current executable path: %v
What it means
upgradeBuild cannot determine the path of the running Caddy executable (os.Executable failed) before replacing it with a freshly downloaded build. Without the current path, the upgrade cannot back up or overwrite the binary, so it aborts with ExitCodeFailedStartup.
Source
Thrown at cmd/packagesfuncs.go:129
if err != nil {
return caddy.ExitCodeFailedStartup, fmt.Errorf("invalid module name: %v", err)
}
if _, ok := pluginPkgs[module]; !ok {
// package does not exist
return caddy.ExitCodeFailedStartup, fmt.Errorf("package is not added")
}
delete(pluginPkgs, arg)
}
return upgradeBuild(pluginPkgs, fl)
}
func upgradeBuild(pluginPkgs map[string]pluginPackage, fl Flags) (int, error) {
l := caddy.Log()
thisExecPath, err := os.Executable()
if err != nil {
return caddy.ExitCodeFailedStartup, fmt.Errorf("determining current executable path: %v", err)
}
thisExecStat, err := os.Stat(thisExecPath)
if err != nil {
return caddy.ExitCodeFailedStartup, fmt.Errorf("retrieving current executable permission bits: %v", err)
}
if thisExecStat.Mode()&os.ModeSymlink == os.ModeSymlink {
symSource := thisExecPath
// we are a symlink; resolve it
thisExecPath, err = filepath.EvalSymlinks(thisExecPath)
if err != nil {
return caddy.ExitCodeFailedStartup, fmt.Errorf("resolving current executable symlink: %v", err)
}
l.Info("this executable is a symlink", zap.String("source", symSource), zap.String("target", thisExecPath))
}
l.Info("this executable will be replaced", zap.String("path", thisExecPath))
// build the request URL to download this custom build
qs := url.Values{View on GitHub (pinned to 50e54ee279)
Solutions
- Check /proc is mounted and readable (`ls -l /proc/self/exe`) in the environment running the command
- If the binary file was deleted/renamed, restore it (reinstall) so os.Executable resolves again, then retry
- Prefer upgrading via xcaddy or downloading a new binary to a fresh path rather than in-place upgrade in constrained environments
Defensive patterns
Strategy: validation
Validate before calling
# verify the running executable can be resolved before upgrading ls -l /proc/self/exe >/dev/null 2>&1 || echo '/proc unavailable; in-place upgrade will fail' readlink -f "$(command -v caddy)" >/dev/null || echo 'caddy path unresolvable'
Prevention
- Run in-place upgrades only where /proc is mounted (bare metal, normal containers)
- Keep binaries at stable paths that are not deleted while running
When it happens
Trigger: The running binary was deleted or replaced while the process is alive (common when a prior upgrade attempt half-completed), or /proc is not mounted (os.Executable reads /proc/self/exe on Linux) — e.g. inside minimal containers or gVisor sandboxes.
Common situations: Running `caddy upgrade` in a container without /proc; a package manager replaced the file at the same path; running from a deleted temp directory.
Related errors
- setting environment variables: %v
- unable to enumerate installed plugins: %v
- retrieving current executable permission bits: %v
- resolving current executable symlink: %v
- download failed: %v
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/071d207f3158decd.
Report an issue: GitHub.