gravitational/teleport · warning

executable has unstable path

Error message

executable has unstable path

What it means

ErrUnstableExecutable is returned by StableExecutable/stablePathForBinary when a stable, version-independent path to the Teleport binary cannot be resolved. The unstable path (from os.Executable()) is still returned alongside the error so callers can proceed, but the path points at a specific versioned binary that may disappear after an update.

Source

Thrown at lib/autoupdate/agent/integrations.go:40

	"context"
	"errors"
	"io/fs"
	"log/slog"
	"os"
	"path/filepath"

	"github.com/google/uuid"
	"github.com/gravitational/trace"

	"github.com/gravitational/teleport/api/types"
)

var (
	// ErrConfigNotFound is returned by HellorUpdaterInfo when the updater config file cannot be found.
	ErrConfigNotFound = errors.New("updater config file not found")

	// ErrUnstableExecutable is returned by StableExecutable when no stable path can be found.
	ErrUnstableExecutable = errors.New("executable has unstable path")
)

const updateConfigFileEnvVar = "TELEPORT_UPDATE_CONFIG_FILE"

// IsManagedByUpdater returns true if the local Teleport binary is managed by teleport-update.
// Note that true may be returned even if auto-updates is disabled or the version is pinned.
// The binary is considered managed if it lives under /opt/teleport, but not within the package
// path at /opt/teleport/system.
func IsManagedByUpdater() (bool, error) {
	systemd, err := hasSystemD()
	if err != nil {
		return false, trace.Wrap(err)
	}
	if !systemd {
		return false, nil
	}
	teleportPath, err := os.Executable()
	if err != nil {

View on GitHub (pinned to 1283425b60)

Solutions

  1. For package installs, recreate the stable symlink: ln -s /opt/teleport/system/bin/teleport /usr/local/bin/teleport (matching name).
  2. For Managed Updates, inspect update.yaml (cfg.Spec.Path) and ensure the path exists and contains the binary; re-run teleport-update to repair links.
  3. Re-run the installer/upgrade to restore the stable link path, or hard-restart the service so it resolves the new binary.
  4. Handle the error in callers: use the returned unstable path but treat it as ephemeral — do not persist it across updates.

Example fix

// before
p, err := autoupdate.StableExecutable()
if err != nil { return err }
// after
p, err := autoupdate.StableExecutable()
if errors.Is(err, autoupdate.ErrUnstableExecutable) {
    // p is still valid but version-specific; use it, don't persist it
} else if err != nil {
    return err
}
Defensive patterns

Strategy: type-guard

Validate before calling

// Pre-check the stable link before calling:
if _, err := os.Stat("/usr/local/bin/teleport"); err != nil {
    // stable link missing: repair it or expect ErrUnstableExecutable
}

Type guard

func isUnstableExecutable(err error) bool { return errors.Is(err, autoupdate.ErrUnstableExecutable) }

Try / catch

p, err := autoupdate.StableExecutable()
switch {
case errors.Is(err, autoupdate.ErrUnstableExecutable):
    // p is valid but version-specific; use ephemeral, do not persist
case err != nil:
    return trace.Wrap(err)
}

Prevention

When it happens

Trigger: Calling StableExecutable when: (1) the binary lives in a package install (<packageSystemDir>/bin/<name>) but /usr/local/bin/<name> does not exist or does not stat (integrations.go:103); or (2) the binary is a Managed Updates install and update.yaml exists but cfg.Spec.Path is empty or the target link path <cfg.Spec.Path>/<name> fails os.Stat (integrations.go:122).

Common situations: Package (deb/rpm) installs where the /usr/local/bin symlink was deleted or never created; Managed Updates installs whose update.yaml points to a path that was moved, deleted, or never linked; partially completed upgrades leaving the versions dir without a working stable link.

Related errors


AI-assisted analysis of gravitational/teleport@1283425b60 (2026-09-02). Data as JSON: /api/errors/4da5c536aff67304. Report an issue: GitHub.