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

  1. Check the binary manually: file $(which caddy) and run 'caddy version' to see the raw exec error
  2. Re-run the upgrade — a corrupted download is usually transient
  3. Verify disk space: df -h on the binary's filesystem
  4. If the platform build is wrong, download a standard release binary from GitHub releases instead of the custom build API
  5. 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

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


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