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

  1. Check /proc is mounted and readable (`ls -l /proc/self/exe`) in the environment running the command
  2. If the binary file was deleted/renamed, restore it (reinstall) so os.Executable resolves again, then retry
  3. 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

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


AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15). Data as JSON: /api/errors/071d207f3158decd. Report an issue: GitHub.