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
- Treat it as success in scripts: `gh extension upgrade <name> || [ $? -eq 1 ]`-style guards or match the 'already up to date' message.
- If you expected a newer version, refresh metadata: `gh extension list` triggers a fetch, or reinstall with `gh extension install <owner>/<gh-name> --force`.
- 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
- Use `gh extension upgrade` (all) in schedulers; it reports up-to-date items without failing.
- Treat 'already up to date' as exit-success in idempotent automation.
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
- pinned extensions can not be upgraded
- local extensions can not be upgraded
- some extensions failed to upgrade
- failed to find selected entry
- no extensions found
AI-assisted analysis of cli/cli@0eeec0b92e (2026-08-15).
Data as JSON: /api/errors/9b043a88ea976353.
Report an issue: GitHub.