matryer/xbar · error
starting new app failed: exit code %d
Error message
starting new app failed: exit code %d
What it means
This is a wrapped error from Updater.Restart (pkg/update/update.go:102) produced when cmd.Start() fails while spawning the freshly updated application, specifically when the error is an *exec.ExitError carrying an exit code. Note cmd.Start() rarely returns ExitError (it reports start failures); the format string includes the child's exit code to indicate the new process exited immediately with that code. The plain "starting new app failed" variant is wrapped when the error is not an ExitError.
Source
Thrown at pkg/update/update.go:102
thisExecuable, err := os.Executable()
if err != nil {
return errors.Wrap(err, "get executable")
}
log.Println("restarting", thisExecuable)
cmd := exec.Command(thisExecuable)
cmd.SysProcAttr = &syscall.SysProcAttr{
Setpgid: false,
}
cmd.Dir = filepath.Dir(thisExecuable)
cmd.Env = os.Environ()
cmd.Env = append(cmd.Env, "XBAR_UPDATE_RESTART_COUNTER=1")
cmd.Args = os.Args
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Start()
if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
return errors.Wrapf(err, "starting new app failed: exit code %d", exitErr.ExitCode())
}
return errors.Wrap(err, "starting new app failed")
}
log.Println("waiting before terminating after update...")
time.Sleep(1 * time.Second)
log.Println("terminating after update.")
os.Exit(0)
return nil
}
// getLatestRelease gets the latest release.
func (u *Updater) getLatestRelease() (*Release, error) {
resp, err := u.Client.Get(u.LatestReleaseGitHubEndpoint)
if err != nil {
return nil, errors.Wrap(err, "get latest release")
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {View on GitHub (pinned to d624239058)
Solutions
- Check the reported exit code and the new app's stderr (it is wired to os.Stderr) for the startup failure cause
- Verify the replaced binary is executable and matches the host OS/arch (chmod +x, correct build target)
- Roll back to the previous binary and re-release a fixed build
- Ensure the new version runs standalone: test the same command (os.Args) manually on the host
- Check container resource limits (ulimit, cgroup PIDs/memory) if exec fails with EAGAIN/permission errors
Example fix
// before
err = cmd.Start()
if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
return errors.Wrapf(err, "starting new app failed: exit code %d", exitErr.ExitCode())
}
return errors.Wrap(err, "starting new app failed")
}
// after
if err := os.Chmod(thisExecuable, 0o755); err != nil {
return errors.Wrap(err, "fix binary permissions")
}
err = cmd.Start()
if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
return errors.Wrapf(err, "starting new app failed: exit code %d", exitErr.ExitCode())
}
return errors.Wrap(err, "starting new app failed")
} Defensive patterns
Strategy: try-catch
Validate before calling
if fi, err := os.Stat(newBinaryPath); err != nil || fi.Mode()&0o111 == 0 {
return fmt.Errorf("new binary missing or not executable: %s", newBinaryPath)
}
if runtime.GOOS != buildOS || runtime.GOARCH != buildArch {
return fmt.Errorf("asset arch mismatch: %s/%s", buildOS, buildArch)
} Type guard
var exitErr *exec.ExitError
if errors.As(err, &exitErr) {
log.Printf("new app exited with code %d", exitErr.ExitCode())
} Try / catch
if err := updater.Restart(); err != nil {
if strings.Contains(fmt.Sprintf("%+v", err), "starting new app failed") {
log.Printf("updated binary failed to start; rolling back: %v", err)
restorePreviousBinary()
return updater.Restart()
}
return err
} Prevention
- Smoke-test each release binary on the target OS/arch before publishing
- Preserve executable permissions when replacing the binary (rename into place, chmod 0755)
- Ship static builds to avoid missing shared-library failures at startup
- Wire the child's stderr (as Restart does) to capture the new app's startup errors
- Implement a rollback path if the new binary exits immediately with a nonzero code
When it happens
Trigger: Restart() configures exec.Command(thisExecuable) with os.Args/Stdout/Stderr and calls cmd.Start() (pkg/update/update.go:102); the new binary fails to launch or exits immediately — e.g. updated binary not executable (missing +x), wrong architecture, missing dynamic libraries, immediately-crashing new version, or resource limits (fork/exec failure).
Common situations: Update replaced the binary with one built for the wrong OS/arch; permissions lost during replace (chmod not preserved) so exec fails with permission denied; new build crashes at startup (bad config, missing migration); cgroup/memory limits preventing fork in containers; EAGAIN under heavy process limits.
Related errors
AI-assisted analysis of matryer/xbar@d624239058 (2026-09-02).
Data as JSON: /api/errors/42b6a87208a6f9c9.
Report an issue: GitHub.