cli/cli · error · ErrReleaseNotFound

release not found

Error message

release not found

What it means

Sentinel error (shared.ErrReleaseNotFound) in the release shared package. It is returned when a REST lookup of a release, or of the git tag ref backing it, responds with HTTP 404 - meaning no release with that tag exists on the repo (or is visible to your credentials). Because it is a package-level sentinel, callers match it with errors.Is, e.g. `gh release create` checks it to decide whether to create a new release or update an existing one.

Source

Thrown at pkg/cmd/release/shared/fetch.go:133

					"digest":        a.Digest,
					"state":         a.State,
					"createdAt":     a.CreatedAt,
					"updatedAt":     a.UpdatedAt,
					"downloadCount": a.DownloadCount,
					"contentType":   a.ContentType,
				})
			}
			data[f] = assets
		default:
			sf := fieldByName(v, f)
			data[f] = sf.Interface()
		}
	}

	return data
}

var ErrReleaseNotFound = errors.New("release not found")

type fetchResult struct {
	release *Release
	error   error
}

func FetchRefSHA(ctx context.Context, httpClient *http.Client, repo ghrepo.Interface, tagName string) (string, error) {
	url, err := safeurl.JoinPathWithHostPrefix(ghinstance.RESTPrefix(repo.RepoHost()), "repos", repo.RepoOwner(), repo.RepoName(), "git", "ref", fmt.Sprintf("tags/%s", tagName))
	if err != nil {
		return "", err
	}
	req, err := http.NewRequestWithContext(ctx, "GET", url.String(), nil)
	if err != nil {
		return "", err
	}

	resp, err := httpClient.Do(req)
	if err != nil {

View on GitHub (pinned to 0eeec0b92e)

Solutions

  1. Verify the tag and repo: gh release list -R owner/repo, or gh release view <tag>
  2. For draft releases, authenticate with an account that has push access to the repo
  3. If you are writing automation, check errors.Is(err, shared.ErrReleaseNotFound) and branch to create vs update logic like gh itself does

Example fix

// before
rel, err := shared.FetchRelease(ctx, client, repo, "v1.2.3")
if err != nil { return err }
// after
rel, err := shared.FetchRelease(ctx, client, repo, "v1.2.3")
if errors.Is(err, shared.ErrReleaseNotFound) {
    // create it instead
} else if err != nil { return err }
Defensive patterns

Strategy: try-catch

Validate before calling

// Cheap existence probe before heavy logic:
out, err := ghapi(fmt.Sprintf("repos/%s/%s/releases/tags/%s", owner, repo, tag))
if err != nil { /* 404 means the release does not exist yet */ }

Try / catch

rel, err := shared.FetchRelease(ctx, client, repo, tag)
switch {
case err == nil:
    // update path
case errors.Is(err, shared.ErrReleaseNotFound):
    // create path (this is exactly what gh release create does)
default:
    return err // real network/auth failure
}

Prevention

When it happens

Trigger: Any FetchRelease/FetchRefSHA call where GitHub returns 404: wrong tag name, release not yet created, a draft release on a repo your token cannot see (drafts require write access), or querying the wrong repository/host.

Common situations: Release automation scripts assuming the release exists; read-only tokens trying to view draft releases; typos in the tag; a fork where the release lives on the upstream repo. Note create/http.go intentionally treats this error as 'release does not exist yet, proceed to create'.

Related errors


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