caddyserver/caddy · error
no build info
Error message
no build info
What it means
getModules() calls debug.ReadBuildInfo(), which returns ok=false when the binary carries no embedded Go build metadata. Without build info, caddy cannot enumerate which plugins (non-standard modules) are compiled in, so 'upgrade', 'add-package', 'remove-package', and 'list-modules' cannot compute the package list to send to the build API.
Source
Thrown at cmd/packagesfuncs.go:219
// clean up the backup file
if !fl.Bool("keep-backup") {
if err = removeCaddyBinary(backupExecPath); err != nil {
return caddy.ExitCodeFailedStartup, fmt.Errorf("download succeeded, but unable to clean up backup binary: %v", err)
}
} else {
l.Info("skipped cleaning up the backup file", zap.String("backup_path", backupExecPath))
}
l.Info("upgrade successful; please restart any running Caddy instances", zap.String("executable", thisExecPath))
return caddy.ExitCodeSuccess, nil
}
func getModules() (standard, nonstandard, unknown []moduleInfo, err error) {
bi, ok := debug.ReadBuildInfo()
if !ok {
err = fmt.Errorf("no build info")
return standard, nonstandard, unknown, err
}
for _, modID := range caddy.Modules() {
modInfo, err := caddy.GetModule(modID)
if err != nil {
// that's weird, shouldn't happen
unknown = append(unknown, moduleInfo{caddyModuleID: modID, err: err})
continue
}
// to get the Caddy plugin's version info, we need to know
// the package that the Caddy module's value comes from; we
// can use reflection but we need a non-pointer value (I'm
// not sure why), and since New() should return a pointer
// value, we need to dereference it first
iface := any(modInfo.New())
if rv := reflect.ValueOf(iface); rv.Kind() == reflect.Pointer {View on GitHub (pinned to 50e54ee279)
Solutions
- Build from a proper module checkout: git clone + xcaddy build (or go install github.com/caddyserver/caddy/cmd/caddy@latest from within any module)
- If distro-packaged, prefer the official binaries from caddyserver.com or GitHub releases, then use 'caddy upgrade'
- If stripping is deliberate (garble, strip), accept that package-management commands are unavailable and rebuild manually with xcaddy to change plugins
Example fix
# before strip /usr/local/bin/caddy # removes build info caddy upgrade # error: no build info # after xcaddy build # produces a binary with build info caddy upgrade
Defensive patterns
Strategy: validation
Validate before calling
go version -m "$(command -v caddy)" >/dev/null 2>&1 || echo 'binary has no build info; rebuild with xcaddy'
Prevention
- Build custom binaries with xcaddy so build info is embedded
- Never strip caddy binaries (strip -s, garble) if you plan to use upgrade/add-package
- Verify with 'go version -m $(which caddy)' before relying on package management commands
When it happens
Trigger: The caddy binary was compiled in a way that strips build info: built with 'go build -ldflags="-s -w"' plus manual stripping, built in GOPATH mode (go run from a non-module dir), produced by a tool that rewrites the binary (some packers/obfuscators like garble), or assembled from objects without module context.
Common situations: Using a caddy binary repackaged by a Linux distribution that strips binaries (strip(1) removes build info); custom builds hardened with garble; compiling after copying sources out of a module tree.
Related errors
- unable to enumerate installed plugins: %v
- determining current executable path: %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/b59f80f2c7df7176.
Report an issue: GitHub.