cli/cli · info · upToDateError

already up to date

Error message

already up to date

What it means

Defined as the package-level sentinel upToDateError and returned by Manager.upgradeExtension when the extension is not pinned/local and its currently installed version already matches the latest available version (UpdateAvailable() false). It signals a no-op rather than a failure; when upgrading all extensions, gh catches it and prints 'already up to date' per extension instead of failing, but when you upgrade a single named extension the error surfaces to the command and exits nonzero.

Source

Thrown at pkg/cmd/extension/manager.go:456

	}

	scopedClient := m.gitClient.ForRepo(targetDir)
	err = scopedClient.CheckoutBranch(commitSHA)
	if err != nil {
		return err
	}

	pinPath := filepath.Join(targetDir, fmt.Sprintf(".pin-%s", commitSHA))
	f, err := os.OpenFile(pinPath, os.O_WRONLY|os.O_CREATE, 0600)
	if err != nil {
		return fmt.Errorf("failed to create pin file in directory: %w", err)
	}
	return f.Close()
}

var pinnedExtensionUpgradeError = errors.New("pinned extensions can not be upgraded")
var localExtensionUpgradeError = errors.New("local extensions can not be upgraded")
var upToDateError = errors.New("already up to date")
var noExtensionsInstalledError = errors.New("no extensions installed")

func (m *Manager) Upgrade(name string, force bool) error {
	// Fetch metadata during list only when upgrading all extensions.
	// This is a performance improvement so that we don't make a
	// bunch of unnecessary network requests when trying to upgrade a single extension.
	fetchMetadata := name == ""
	exts, _ := m.list(fetchMetadata)
	if len(exts) == 0 {
		return noExtensionsInstalledError
	}
	if name == "" {
		return m.upgradeExtensions(exts, force)
	}
	for _, f := range exts {
		if f.Name() != name {
			continue
		}

View on GitHub (pinned to 0eeec0b92e)

Solutions

  1. Treat it as success in scripts: `gh extension upgrade <name> || [ $? -eq 1 ]`-style guards or match the 'already up to date' message.
  2. If you expected a newer version, refresh metadata: `gh extension list` triggers a fetch, or reinstall with `gh extension install <owner>/<gh-name> --force`.
  3. For scheduled upgrades prefer `gh extension upgrade --all`, which reports up-to-date extensions without failing.

Example fix

# before (cron)
gh extension upgrade gh-changelog || exit 1   # fails when current

# after
gh extension upgrade gh-changelog || grep -q 'already up to date' /dev/stdin || true
# simpler: use bulk mode which tolerates it
gh extension upgrade --all
Defensive patterns

Strategy: try-catch

Validate before calling

# bash: compare current vs latest before upgrading (non-TTY tolerant)
ver=$(gh extension list --json name,version,latestVersion \
  -q ".[] | select(.name==\"gh-changelog\") | if .version == .latestVersion then \"current\" else \"stale\" end")
[[ "$ver" == current ]] && { echo 'already current'; exit 0; }
gh extension upgrade gh-changelog

Try / catch

out=$(gh extension upgrade gh-changelog 2>&1); rc=$?
if [[ $rc -eq 0 ]]; then echo upgraded
elif [[ "$out" == *'already up to date'* ]]; then echo 'no-op: current'; exit 0
else echo "$out" >&2; exit $rc
fi

Prevention

When it happens

Trigger: Running `gh extension upgrade <name>` when the local commit/semantic version equals the remote HEAD/latest release. In the upgrade-all path (name == ""), the error is detected via errors.Is and printed as an informational line; in the single-extension path it propagates.

Common situations: Idempotent cron/CI jobs that upgrade on a schedule and hit the sentinel on every run after the first; users manually upgrading right after installing; stale local metadata making gh believe it is current when a newer version exists (fix by re-listing with network).

Related errors


AI-assisted analysis of cli/cli@0eeec0b92e (2026-08-15). Data as JSON: /api/errors/9b043a88ea976353. Report an issue: GitHub.