{"record":{"id":"7b7fa581699d6f6a","repo":"caddyserver/caddy","slug":"resolving-current-executable-symlink-v","errorCode":null,"errorMessage":"resolving current executable symlink: %v","messagePattern":"resolving current executable symlink: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/packagesfuncs.go","lineNumber":140,"sourceCode":"}\n\nfunc upgradeBuild(pluginPkgs map[string]pluginPackage, fl Flags) (int, error) {\n\tl := caddy.Log()\n\n\tthisExecPath, err := os.Executable()\n\tif err != nil {\n\t\treturn caddy.ExitCodeFailedStartup, fmt.Errorf(\"determining current executable path: %v\", err)\n\t}\n\tthisExecStat, err := os.Stat(thisExecPath)\n\tif err != nil {\n\t\treturn caddy.ExitCodeFailedStartup, fmt.Errorf(\"retrieving current executable permission bits: %v\", err)\n\t}\n\tif thisExecStat.Mode()&os.ModeSymlink == os.ModeSymlink {\n\t\tsymSource := thisExecPath\n\t\t// we are a symlink; resolve it\n\t\tthisExecPath, err = filepath.EvalSymlinks(thisExecPath)\n\t\tif err != nil {\n\t\t\treturn caddy.ExitCodeFailedStartup, fmt.Errorf(\"resolving current executable symlink: %v\", err)\n\t\t}\n\t\tl.Info(\"this executable is a symlink\", zap.String(\"source\", symSource), zap.String(\"target\", thisExecPath))\n\t}\n\tl.Info(\"this executable will be replaced\", zap.String(\"path\", thisExecPath))\n\n\t// build the request URL to download this custom build\n\tqs := url.Values{\n\t\t\"os\":   {runtime.GOOS},\n\t\t\"arch\": {runtime.GOARCH},\n\t}\n\tfor _, pkgInfo := range pluginPkgs {\n\t\tqs.Add(\"p\", pkgInfo.String())\n\t}\n\n\t// initiate the build\n\tresp, err := downloadBuild(qs)\n\tif err != nil {\n\t\treturn caddy.ExitCodeFailedStartup, fmt.Errorf(\"download failed: %v\", err)","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/cmd/packagesfuncs.go#L122-L158","documentation":"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.","triggerScenarios":"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.","commonSituations":"`/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.","solutions":["Inspect the link: `readlink -f $(which caddy)` — if it errors or points nowhere, restore the target binary","Re-download a real binary to the target path (curl the caddyserver.com download API) or rebuild with xcaddy, keeping the symlink intact","Alternatively replace the symlink with the actual binary to avoid future resolution issues"],"exampleFix":"# before\ncaddy upgrade  # /usr/local/bin/caddy -> missing target\n\n# after\ncurl -sSL 'https://caddyserver.com/api/download?os=linux&arch=amd64' -o /opt/caddy/caddy\ncaddy upgrade","handlingStrategy":"validation","validationCode":"P=$(command -v caddy)\nif [ -L \"$P\" ]; then T=$(readlink -f \"$P\") && [ -e \"$T\" ] || echo \"broken symlink: $P -> target missing; restore target first\"; fi","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Point symlinks at durable target paths","After any failed upgrade, check the symlink target still exists before retrying"],"tags":["caddy","upgrade","symlink","filesystem","cli"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}