Skip to content

Some attachments in private repos are not downloaded (HTTPError: Not Found), but can be accessed in a browser session #477

Closed

Description

@lukasbestle

My use

$ github-backup --version
github-backup 0.60.0

$ github-backup --keychain-name github-backup --keychain-account my-org -o /path/to/backup --issues --pulls --attachments --private -O my-org

The keychain contains a fine-grained access token with all permissions according to README (especially "Read access to contents, issues, metadata, pull requests, and webhooks").

Observed behavior

Some attachments in these private repos are failing with:

Skipping download of attachment https://github.com/user-attachments/assets/... due to HTTPError: Not Found
Skipping download of attachment https://github.com/org/repo/assets/.../... due to HTTPError: Not Found

If I open the failed URLs in the browser where I'm logged in to GitHub, the redirects to the actual S3 URLs work fine, so it looks to be an auth issue of some sort.

Legacy URLs of the format https://user-images.githubusercontent.com/* are not affected and could be downloaded correctly in the same run of github-backup while backing up the same private repo. Only the "modern" and "repo assets" patterns seem to be affected.

What I expected to happen

All attachments should be downloaded successfully.

Question

Do I need special scopes for the fine-grained access token in order to be able to download all kinds of attachments?

Activity

  1. Iamrodos commented on Jan 11, 2026

    @Iamrodos
    Contributor

    To help debug this, could you share a bit more info?

    1. Check the manifest.json

    The manifest files contain the actual HTTP status code and full URLs. Could you share an example entry from a failed download?

      find /path/to/backup -name "manifest.json" -exec cat {} \;

    Looking for entries with "success": false - specifically the http_status and url fields.

    1. Manual curl test

    If you can share one of the failing URLs (or reproduce with a public repo), this curl command emulates what the tool does:

      curl -v -H "Authorization: token YOUR_TOKEN" \
           -H "Accept: application/octet-stream" \
           "https://github.com/user-attachments/assets/ACTUAL_UUID"
    

    The -v output would show us the full HTTP response and any redirects.

    1. Token type

    You mentioned using a fine-grained token. Have you tried with a classic PAT to see if the behavior differs?

    1. Debug logging

    Running with --log-level debug might surface additional insight:

    github-backup ... --log-level debug
    
  2. Iamrodos commented on Jan 11, 2026

    @Iamrodos
    Contributor

    Update, I have investigated more by create a fine-grained PAT and can reproduced the issue.

    Root Cause

    This appears to be a GitHub platform limitation with fine-grained PATs and user-attachments URLs in private repos. Classic PATs work consistently, but fine-grained PATs have inconsistent behavior. Others have reported having the same issue with the this when calling the API.

    I tested a private repo with both token types:

    Token Type Result
    Classic PAT (ghp_...) Works for all attachments
    Fine-grained PAT (github_pat_...) Mixed - some 302 (success), some 404

    In my testing, newer attachments worked while older attachments failed with fine-grained PATs. The exact criteria GitHub uses is unclear, can't see why some work and some do not.

    What You Can Do Now

    Use a classic PAT instead of a fine-grained PAT when backing up attachments from private repositories. Classic tokens with repo scope work consistently.

    Potential improvements

    There is a partial workaround using GitHub's Markdown API for /assets/ URLs that gets a JWT that can be used to get image assets only. But this would still leave /files/ assets still not working with a fine-grained PAT.

    I can also document the limitation in the readme on /files/ and issue a warning when warning when --attachments is used with a fine-grained token.

    Let me know how you get on and if you think support images is sufficient enough to worth doing without files. Or if you are going to have to use classic anyway so its not worth it.

  3. lukasbestle commented on Jan 11, 2026

    @lukasbestle
    ContributorAuthor

    Thanks for your investigation.

    IMO the JWT workaround for assets seems worthwhile. Images are a lot more common as issue/PR attachments than other file types. It doesn’t seem warranted to reduce security by using a classic token just for /files/.

    So I think the fix could be a combination of:

    • Implement the workaround so as many attachments as possible can be backed up using a fine-grained token
    • Document the limitation of attachment files in private repos (with solution to use a classic token if these need to be backed up)
  4. Iamrodos commented on Jan 11, 2026

    @Iamrodos
    Contributor

    Thanks for the insight @lukasbestle, I should be able to get this done in the next few days. In the meantime the classic PAT is your friend. Lets leave this open and we can reference in the PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions