caddyserver/caddy · error
download succeeded, but unable to execute 'caddy list-module
Error message
download succeeded, but unable to execute 'caddy list-modules': %v
What it means
After a successful download and write of the new binary, caddy runs '<newexec> list-modules --versions --skip-standard' via exec.Command to display module info. This error means that subprocess failed to start or exited non-zero. The new binary is already in place; the deferred restore is not triggered because err is reassigned... actually it is: the returned err triggers the deferred restore, so the original binary is restored from the .tmp backup.
Source
Thrown at cmd/packagesfuncs.go:194
zap.String("backup_path", backupExecPath),
zap.String("original_path", thisExecPath),
zap.Error(err2))
}
}
}()
// download the file; do this in a closure to close reliably before we execute it
err = writeCaddyBinary(thisExecPath, &resp.Body, thisExecStat)
if err != nil {
return caddy.ExitCodeFailedStartup, err
}
l.Info("download successful; displaying new binary details", zap.String("location", thisExecPath))
// use the new binary to print out version and module info
fmt.Print("\nModule versions:\n\n")
if err = listModules(thisExecPath); err != nil {
return caddy.ExitCodeFailedStartup, fmt.Errorf("download succeeded, but unable to execute 'caddy list-modules': %v", err)
}
fmt.Println("\nVersion:")
if err = showVersion(thisExecPath); err != nil {
return caddy.ExitCodeFailedStartup, fmt.Errorf("download succeeded, but unable to execute 'caddy version': %v", err)
}
fmt.Println()
// 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))
View on GitHub (pinned to 50e54ee279)
Solutions
- Check the binary manually: file $(which caddy) and run 'caddy version' to see the raw exec error
- Re-run the upgrade — a corrupted download is usually transient
- Verify disk space: df -h on the binary's filesystem
- If the platform build is wrong, download a standard release binary from GitHub releases instead of the custom build API
- On macOS/Windows, exclude the caddy path from Gatekeeper/antivirus quarantine
Defensive patterns
Strategy: validation
Validate before calling
# after an upgrade attempt, verify the binary executes before relying on it caddy version && caddy list-modules --versions | head
Prevention
- Keep --keep-backup in mind: passing it retains the .tmp rollback copy
- Ensure the download API build matches your platform (run upgrade on the target host, not a different arch)
- Exclude the caddy path from antivirus quarantine on Windows/macOS
When it happens
Trigger: The freshly written binary cannot be executed: exec format error (wrong GOARCH/GOOS from the build API), the file lost its exec bit (writeCaddyBinary preserves the original mode, so this is rare), a corrupted/truncated download that still wrote to disk, or antivirus/quarantine blocking execution on Windows/macOS.
Common situations: Downloaded build is corrupted by a proxy; the download API returned a build for the wrong platform; macOS Gatekeeper quarantining a newly written binary; disk filled mid-write so the binary is truncated.
Related errors
- download succeeded, but unable to execute 'caddy version': %
- starting caddy process: %v
- unable to enumerate installed plugins: %v
- retrieving current executable permission bits: %v
- resolving current executable symlink: %v
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/313dbc143226b339.
Report an issue: GitHub.