{"record":{"id":"42b6a87208a6f9c9","repo":"matryer/xbar","slug":"starting-new-app-failed-exit-code-d","errorCode":null,"errorMessage":"starting new app failed: exit code %d","messagePattern":"starting new app failed: exit code (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/update/update.go","lineNumber":102,"sourceCode":"\tthisExecuable, err := os.Executable()\n\tif err != nil {\n\t\treturn errors.Wrap(err, \"get executable\")\n\t}\n\tlog.Println(\"restarting\", thisExecuable)\n\tcmd := exec.Command(thisExecuable)\n\tcmd.SysProcAttr = &syscall.SysProcAttr{\n\t\tSetpgid: false,\n\t}\n\tcmd.Dir = filepath.Dir(thisExecuable)\n\tcmd.Env = os.Environ()\n\tcmd.Env = append(cmd.Env, \"XBAR_UPDATE_RESTART_COUNTER=1\")\n\tcmd.Args = os.Args\n\tcmd.Stdout = os.Stdout\n\tcmd.Stderr = os.Stderr\n\terr = cmd.Start()\n\tif err != nil {\n\t\tif exitErr, ok := err.(*exec.ExitError); ok {\n\t\t\treturn errors.Wrapf(err, \"starting new app failed: exit code %d\", exitErr.ExitCode())\n\t\t}\n\t\treturn errors.Wrap(err, \"starting new app failed\")\n\t}\n\tlog.Println(\"waiting before terminating after update...\")\n\ttime.Sleep(1 * time.Second)\n\tlog.Println(\"terminating after update.\")\n\tos.Exit(0)\n\treturn nil\n}\n\n// getLatestRelease gets the latest release.\nfunc (u *Updater) getLatestRelease() (*Release, error) {\n\tresp, err := u.Client.Get(u.LatestReleaseGitHubEndpoint)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"get latest release\")\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode != http.StatusOK {","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/matryer/xbar/blob/d624239058997c80118eaebe2e7f8331b3c765e0/pkg/update/update.go#L84-L120","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// before\nerr = cmd.Start()\nif err != nil {\n\tif exitErr, ok := err.(*exec.ExitError); ok {\n\t\treturn errors.Wrapf(err, \"starting new app failed: exit code %d\", exitErr.ExitCode())\n\t}\n\treturn errors.Wrap(err, \"starting new app failed\")\n}\n// after\nif err := os.Chmod(thisExecuable, 0o755); err != nil {\n\treturn errors.Wrap(err, \"fix binary permissions\")\n}\nerr = cmd.Start()\nif err != nil {\n\tif exitErr, ok := err.(*exec.ExitError); ok {\n\t\treturn errors.Wrapf(err, \"starting new app failed: exit code %d\", exitErr.ExitCode())\n\t}\n\treturn errors.Wrap(err, \"starting new app failed\")\n}","handlingStrategy":"try-catch","validationCode":"if fi, err := os.Stat(newBinaryPath); err != nil || fi.Mode()&0o111 == 0 {\n\treturn fmt.Errorf(\"new binary missing or not executable: %s\", newBinaryPath)\n}\nif runtime.GOOS != buildOS || runtime.GOARCH != buildArch {\n\treturn fmt.Errorf(\"asset arch mismatch: %s/%s\", buildOS, buildArch)\n}","typeGuard":"var exitErr *exec.ExitError\nif errors.As(err, &exitErr) {\n\tlog.Printf(\"new app exited with code %d\", exitErr.ExitCode())\n}","tryCatchPattern":"if err := updater.Restart(); err != nil {\n\tif strings.Contains(fmt.Sprintf(\"%+v\", err), \"starting new app failed\") {\n\t\tlog.Printf(\"updated binary failed to start; rolling back: %v\", err)\n\t\trestorePreviousBinary()\n\t\treturn updater.Restart()\n\t}\n\treturn err\n}","preventionTips":["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"],"tags":["go","process","exec","self-update"],"backgroundTag":"process-start-failed","analyzedSha":"d624239058997c80118eaebe2e7f8331b3c765e0","analyzedAt":"2026-09-02T22:38:22.007Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}