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
- Verify the tag and repo: gh release list -R owner/repo, or gh release view <tag>
- For draft releases, authenticate with an account that has push access to the repo
- 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
- Always branch on errors.Is(err, shared.ErrReleaseNotFound) rather than string matching
- Use write-access tokens when scripts must see draft releases
- Verify tag spelling and -R owner/repo before release automation runs
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
- failed to get session: %s
- no new commits since the last release
- could not find key "oauth_token"
- could not find any host configurations
- set the GH_HOST environment variable to specify which GitHub
AI-assisted analysis of cli/cli@0eeec0b92e (2026-08-15).
Data as JSON: /api/errors/69eb8bb055d4a796.
Report an issue: GitHub.