matryer/xbar · error

get executable

Error message

get executable

What it means

This error is wrapped by Updater.Restart (pkg/update/update.go:86) when os.Executable() fails to resolve the path of the currently running executable. Restart needs this path to spawn a fresh instance after an update; without it the restart cannot proceed. os.Executable fails rarely, typically when the binary has been deleted or renamed while running.

Source

Thrown at pkg/update/update.go:86

		}
	}
	if selectedAsset == nil {
		return nil, errors.New("no asset selected, use SelectAssetFunc to select an asset")
	}
	err = u.downloadAndReplaceApp(*selectedAsset)
	if err != nil {
		return nil, errors.Wrap(err, "download update")
	}
	return latest, nil
}

// Restart spawns the current executable again, and terminates
// the running one.
func (u *Updater) Restart() error {
	time.Sleep(1 * time.Second)
	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")

View on GitHub (pinned to d624239058)

Solutions

  1. Keep the original executable on disk until after Restart completes (replace, don't delete, before restarting)
  2. Restart the process externally (systemd, supervisor, docker restart) instead of self-restarting when running in minimal containers
  3. If running under a deleted path, exec the new binary directly rather than relying on os.Executable()
  4. On Linux, verify /proc/self/exe resolves before calling Restart
  5. Log the underlying errno from the wrapped error to confirm the deleted-binary scenario

Example fix

// before
if err := updater.Restart(); err != nil {
	log.Fatal(err)
}
// after
if err := updater.Restart(); err != nil {
	log.Printf("self-restart failed (%v); requesting supervisor restart", err)
	os.Exit(0) // let systemd/supervisor restart the updated binary
}
Defensive patterns

Strategy: fallback

Validate before calling

exe, err := os.Executable()
if err != nil {
	if _, statErr := os.Stat("/proc/self/exe"); statErr != nil {
		return fmt.Errorf("cannot resolve executable path; self-restart unavailable: %w", err)
	}
}
if strings.Contains(exe, " (deleted)") {
	return fmt.Errorf("binary deleted on disk; external restart required")
}

Try / catch

if err := updater.Restart(); err != nil {
	if strings.Contains(fmt.Sprintf("%+v", err), "get executable") {
		log.Printf("self-restart impossible (%v); exiting for supervisor restart", err)
		os.Exit(0)
	}
	return err
}

Prevention

When it happens

Trigger: Restart() calls os.Executable() (pkg/update/update.go:86) and the OS returns an error — usually because the executable file was deleted or replaced/unlinked from disk after the process started, or on some platforms when /proc-style lookup fails (deleted inode, restricted procfs in minimal containers).

Common situations: Auto-update deleted the old binary and then a restart is attempted in the same process; running inside a minimal container (distroless/scratch) where /proc/self/exe lookup is unavailable or the path no longer exists; binary run via a symlink whose target was removed; deleted-but-running process (deleted suffix path).

Related errors


AI-assisted analysis of matryer/xbar@d624239058 (2026-09-02). Data as JSON: /api/errors/5cf2b96b7e267805. Report an issue: GitHub.