Bug: get_assets() fails with 404 when release is fetched by tag in PyGithub 2.9.0
Summary
In PyGithub 2.9.0, when a release is fetched by tag name using repo.get_release("v1.2.3"), calling release.get_assets() results in a 404 error because the constructed URL is invalid.
Root Cause
PyGithub 2.9.0 introduced lazy loading for releases (PR #3403). When fetching a release by tag:
- The code constructs a lazy object with URL:
https://api.github.com/repos/owner/repo/releases/tags/TAG_NAME
- The
__postProcess method in Requester.py sets data["url"] to this GET URL (line 883)
- The
get_assets() method constructs the assets URL as f"{self.url}/assets"
- This results in:
https://api.github.com/repos/owner/repo/releases/tags/TAG_NAME/assets
- This is not a valid GitHub API endpoint - it should be:
https://api.github.com/repos/owner/repo/releases/RELEASE_ID/assets
Reproduction
from github import Github
gh = Github(auth=...) # Use authenticated client
repo = gh.get_repo("owner/repo")
# Get release by tag
release = repo.get_release("v1.2.3")
# This fails with 404
assets = list(release.get_assets())
Expected Behavior
The release.url property should contain the canonical release URL with the numeric release ID, not the /tags/TAG_NAME URL used to fetch it.
Actual Behavior
github.GithubException.UnknownObjectException: 404 {"message": "Not Found", ...}
Workaround
Get the release by ID instead of by tag:
# Get the release ID first
releases = repo.get_releases()
for r in releases:
if r.tag_name == "v1.2.3":
release = repo.get_release(r.id)
break
Suggested Fix
In Requester.py, the __postProcess() method should:
- Check if the response contains an
id field
- If URL ends with
/releases/tags/{tag}, reconstruct it as /releases/{id}
- Set
data["url"] to the canonical URL
Alternatively, ensure the GitHub API response always includes the proper url field, and only fallback to the request URL if it's missing.
Environment
- PyGithub version: 2.9.0
- Python version: 3.11+
- Works correctly in: PyGithub 2.8.1
Impact
This bug affects any code that:
- Fetches releases by tag name (common pattern)
- Attempts to download release assets
- Uses GitHub App authentication with private repositories (where assets require authentication)
Related Changes
Bug:
get_assets()fails with 404 when release is fetched by tag in PyGithub 2.9.0Summary
In PyGithub 2.9.0, when a release is fetched by tag name using
repo.get_release("v1.2.3"), callingrelease.get_assets()results in a 404 error because the constructed URL is invalid.Root Cause
PyGithub 2.9.0 introduced lazy loading for releases (PR #3403). When fetching a release by tag:
https://api.github.com/repos/owner/repo/releases/tags/TAG_NAME__postProcessmethod inRequester.pysetsdata["url"]to this GET URL (line 883)get_assets()method constructs the assets URL asf"{self.url}/assets"https://api.github.com/repos/owner/repo/releases/tags/TAG_NAME/assetshttps://api.github.com/repos/owner/repo/releases/RELEASE_ID/assetsReproduction
Expected Behavior
The
release.urlproperty should contain the canonical release URL with the numeric release ID, not the/tags/TAG_NAMEURL used to fetch it.Actual Behavior
Workaround
Get the release by ID instead of by tag:
Suggested Fix
In
Requester.py, the__postProcess()method should:idfield/releases/tags/{tag}, reconstruct it as/releases/{id}data["url"]to the canonical URLAlternatively, ensure the GitHub API response always includes the proper
urlfield, and only fallback to the request URL if it's missing.Environment
Impact
This bug affects any code that:
Related Changes
GETurl or_links.selfas object url #3421: "UseGETurl or_links.selfas object url" - introduced the__postProcessmethod