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

  1. Check the reported exit code and the new app's stderr (it is wired to os.Stderr) for the startup failure cause
  2. Verify the replaced binary is executable and matches the host OS/arch (chmod +x, correct build target)
  3. Roll back to the previous binary and re-release a fixed build
  4. Ensure the new version runs standalone: test the same command (os.Args) manually on the host
  5. 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

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.