Fix template URL for watched repositories backup#521
Conversation
|
Thanks for this, I just opened issue #522 not realising you'd already created a PR! |
|
Mind fixing the lint issue and maybe adding a test if possible? /cc @Iamrodos |
|
The endpoint change looks right — I verified the changelog and can reproduce the 204 today, so One thing worth flagging: this is potentially a breaking change for anyone backing up an account other than their own — through no fault of this PR. GitHub removed the ability to read another user's subscriptions entirely, so The diff at the end of this comment follows that existing pattern. It also fixes the lint failure as a side effect — flake8 is flagging the now-unused Feel free to apply the diff to this PR directly — or take whatever pieces of it you find useful. If it's easier, I'm also happy to open a PR against your fork's branch so the changes land here with your authorship intact. On tests: the useful one here is a regression guard that pins the endpoint choice, using the suite's existing mocked- --- a/github_backup/cli.py
+++ b/github_backup/cli.py
@@ -81,7 +81,7 @@ def main():
repositories = retrieve_repositories(args, authenticated_user)
repositories = filter_repositories(args, repositories)
backup_repositories(args, output_directory, repositories)
- backup_account(args, output_directory)
+ backup_account(args, output_directory, authenticated_user)
if __name__ == "__main__":
--- a/github_backup/github_backup.py
+++ b/github_backup/github_backup.py
@@ -3063,8 +3063,10 @@ def fetch_repository(
logging_subprocess(git_command, cwd=local_dir)
-def backup_account(args, output_directory):
+def backup_account(args, output_directory, authenticated_user=None):
account_cwd = os.path.join(output_directory, "account")
+ if authenticated_user is None:
+ authenticated_user = {"login": None}
if args.include_starred or args.include_everything:
output_file = "{0}/starred.json".format(account_cwd)
@@ -3074,11 +3076,25 @@ def backup_account(args, output_directory):
_backup_data(args, "starred repositories", template, output_file, account_cwd)
if args.include_watched or args.include_everything:
- output_file = "{0}/watched.json".format(account_cwd)
- template = "https://{0}/users/{1}/subscriptions".format(
- get_github_api_host(args), args.user
- )
- _backup_data(args, "watched repositories", template, output_file, account_cwd)
+ # GitHub deprecated /users/{username}/subscriptions (June 2026 changelog);
+ # it now returns empty responses. Watched repositories remain available
+ # only for the authenticated user, via /user/subscriptions.
+ if (
+ not authenticated_user.get("login")
+ or args.user.lower() != authenticated_user["login"].lower()
+ ):
+ logger.warning(
+ "Cannot retrieve watched repositories for '%s'. GitHub only allows access to the authenticated user's watched repositories.",
+ args.user,
+ )
+ else:
+ output_file = "{0}/watched.json".format(account_cwd)
+ template = "https://{0}/user/subscriptions".format(
+ get_github_api_host(args)
+ )
+ _backup_data(
+ args, "watched repositories", template, output_file, account_cwd
+ )
if args.include_followers or args.include_everything:
output_file = "{0}/followers.json".format(account_cwd) |
|
Two more nit picky things to consider, both of which also apply to the original one-line fix — flagging, not blocking:
|
|
If a GHE user complains, they can send us money to buy pizza or something to fix it :D |
|
Applied the diff and fixed the lint issue. Sorry about not noticing about Github's breaking change of reading subscriptions of any other user. I should have noticed that when applied the change. |
Summary: github-backup --watched currently fails for personal account backups because GitHub has started returning empty responses from:
GET /users/{username}/subscriptionsThis is now expected behavior per GitHub’s June 30, 2026 changelog:
GitHub states that /users/{username}/subscriptions is being deprecated and that, during the deprecation period, it may return empty responses or 403 Forbidden.
Reproduction
Current result:
The token is valid and includes the relevant permissions. The same token works with the authenticated-user endpoint:
That returns 200 OK with the watched repository JSON array.
Root Cause
--watched uses:
/users/{username}/subscriptionsThat endpoint is no longer reliable for retrieving watched repositories, even when {username} is the authenticated user.
For backing up the authenticated user’s own watched repositories, GitHub now expects:
/user/subscriptionsFix
Changed endpoint