caddyserver/caddy · error

resolving current executable symlink: %v

Error message

resolving current executable symlink: %v

What it means

The running Caddy executable is a symlink (detected via the stat mode bits) and filepath.EvalSymlinks failed while resolving it to the real target. The upgrade must replace the actual file, not the link, so an unresolvable symlink aborts the operation.

Source

Thrown at cmd/packagesfuncs.go:140

}

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{
		"os":   {runtime.GOOS},
		"arch": {runtime.GOARCH},
	}
	for _, pkgInfo := range pluginPkgs {
		qs.Add("p", pkgInfo.String())
	}

	// initiate the build
	resp, err := downloadBuild(qs)
	if err != nil {
		return caddy.ExitCodeFailedStartup, fmt.Errorf("download failed: %v", err)

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Inspect the link: `readlink -f $(which caddy)` — if it errors or points nowhere, restore the target binary
  2. Re-download a real binary to the target path (curl the caddyserver.com download API) or rebuild with xcaddy, keeping the symlink intact
  3. Alternatively replace the symlink with the actual binary to avoid future resolution issues

Example fix

# before
caddy upgrade  # /usr/local/bin/caddy -> missing target

# after
curl -sSL 'https://caddyserver.com/api/download?os=linux&arch=amd64' -o /opt/caddy/caddy
caddy upgrade
Defensive patterns

Strategy: validation

Validate before calling

P=$(command -v caddy)
if [ -L "$P" ]; then T=$(readlink -f "$P") && [ -e "$T" ] || echo "broken symlink: $P -> target missing; restore target first"; fi

Prevention

When it happens

Trigger: A broken symlink (target deleted — often the aftermath of a previous upgrade that moved the real binary to `.tmp`), a symlink loop, or a symlink chain crossing into an unreachable mount.

Common situations: `/usr/local/bin/caddy -> /opt/caddy/caddy` where /opt/caddy/caddy was removed; nix/homebrew-style symlinks whose target was garbage-collected; containers with dangling symlinked binaries.

Related errors


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