Skip to content

github download badges invalid #11216

Open

Description

@smallprogram

Are you experiencing an issue with...

shields.io

🐞 Description

https://img.shields.io/github/downloads/smallprogram/OpenWrtAction/total

The Shields.io GitHub application has been authorized. However, the download badge shows invalid, and it only works for displaying the download count for a single release.

🔗 Link to the badge

GitHub Downloads (all assets, all releases)

💡 Possible Solution

No response

Activity

  1. added
    questionSupport questions, usage questions, unconfirmed bugs, discussions, ideas
    on Jul 18, 2025
  2. jNullj commented on Jul 18, 2025

    @jNullj
    Member

    We have a limit on API response, for example, at the default config the limit is 10MB.
    I confirmed that that response is larger then 10MB and that's what causes the invalid response as we cancel the request to Github's API once we reach that 10MB limit.

    I will try to look into that later today.

  3. chris48s commented on Jul 18, 2025

    @chris48s
    Member

    GitHub only expose download count at release asset level. They don't directly expose an aggregate count. The rest API doesn't provide a way to only request certain fields. We have to accept the full release object to get that one field we are interested in. In the case of this particular project, a single release object is very large because every release has hundreds of related assets.

    One thing we could possibly look at doing is switching to the v4/GraphQL API (with GithubAuthV4Service). Using that, we might be able to write a query that only requests the ReleaseAsset.downloadCount field. That would return a much smaller response. I haven't tested this, but I think now that GitHub have dropped the max number of objects per page to 100 on the Rest API (even though the code still says per_page=500 GitHub now caps it at 100 objects - see https://docs.github.com/en/rest/releases/releases?apiVersion=2022-11-28#list-releases ) we could request the same number of releases in one query over the GraphQL API as we get back from the rest API. As I say, I have not checked it but worth a look.

    In general, the 10Mb limit is there for a reason. It exists to keep performance and memory usage under control. If we have to download and parse >10Mb of data to serve 1 number, that is not a scalable badge. Even if we increased the limit, we probably couldn't serve this badge within camo's 4 second timeout. I am finding that just downloading https://api.github.com/repos/smallprogram/OpenWrtAction/releases?per_page=100 sometimes takes 5 or 6 seconds.

  4. smallprogram commented on Jul 18, 2025

    @smallprogram
    Author

    We have a limit on API response, for example, at the default config the limit is 10MB. I confirmed that that response is larger then 10MB and that's what causes the invalid response as we cancel the request to Github's API once we reach that 10MB limit.

    I will try to look into that later today.

    I think besides the 10MB limit, the problem is here

    static transform({ releases, assetName }) {
    const downloads = releases.reduce((accum1, { assets }) => {
    const filteredAssets =
    assetName === 'total'
    ? assets
    : assets.filter(
    ({ name }) => name.toLowerCase() === assetName.toLowerCase(),
    )
    return (
    accum1 +
    filteredAssets.reduce(
    (accum2, { download_count: downloads }) => accum2 + downloads,
    0,
    )
    )
    }, 0)
    return { downloads }
    }

    You can change the logic to paginate requests, 100 per page, until there are no more, and then continue your reduce() operation.
    of course this is just my idea

  5. smallprogram commented on Jul 18, 2025

    @smallprogram
    Author

    GitHub only expose download count at release asset level. They don't directly expose an aggregate count. The rest API doesn't provide a way to only request certain fields. We have to accept the full release object to get that one field we are interested in. In the case of this particular project, a single release object is very large because every release has hundreds of related assets.

    One thing we could possibly look at doing is switching to the v4/GraphQL API (with GithubAuthV4Service). Using that, we might be able to write a query that only requests the ReleaseAsset.downloadCount field. That would return a much smaller response. I haven't tested this, but I think now that GitHub have dropped the max number of objects per page to 100 on the Rest API (even though the code still says per_page=500 GitHub now caps it at 100 objects - see https://docs.github.com/en/rest/releases/releases?apiVersion=2022-11-28#list-releases ) we could request the same number of releases in one query over the GraphQL API as we get back from the rest API. As I say, I have not checked it but worth a look.

    In general, the 10Mb limit is there for a reason. It exists to keep performance and memory usage under control. If we have to download and parse >10Mb of data to serve 1 number, that is not a scalable badge. Even if we increased the limit, we probably couldn't serve this badge within camo's 4 second timeout. I am finding that just downloading https://api.github.com/repos/smallprogram/OpenWrtAction/releases?per_page=100 sometimes takes 5 or 6 seconds.

    In fact, the slow display speed is not the primary problem, but the error in counting the number of downloads is what needs to be solved urgently.

  6. chris48s commented on Jul 18, 2025

    @chris48s
    Member

    For performance reasons, we don't write badges that paginate over an unbounded number of responses. We have to be able to return a response quickly.
    Ideally, we don't write badges that make more than one upstream request to serve a response. There are a few cases where we make more than one, but it is always a finite number.

    That's why we only take into account the most recent N releases for "total" downloads.

  7. smallprogram commented on Jul 18, 2025

    @smallprogram
    Author

    I hope you geniuses can find a better way to solve this problem.

  8. added
    bugBugs in badges and the frontend
    service-badgeNew or updated service badge
    and removed
    questionSupport questions, usage questions, unconfirmed bugs, discussions, ideas
    on Dec 25, 2025
  9. heliocarlitos commented on Feb 15, 2026

    @heliocarlitos
  10. ashif323 commented on May 5, 2026

    @ashif323
    Contributor

    Hi @chris48s @jNullj 👋
    I'd like to take a stab at fixing this using the GraphQL approach you outlined. Before I start, a couple of quick questions:

    For the GraphQL query, I'm planning to request only ReleaseAsset.downloadCount per release — is there a max number of releases per page you'd be comfortable with (e.g. 100)?
    Since GitHub GraphQL also caps at 100 nodes per page, repos with more than 100 releases would still require pagination. Should I cap at the first 100 releases (matching the current REST behavior) or is there an acceptable way to handle multiple pages here?
    Should this fix cover both the total and per-asset-name cases in transform()?

    Happy to proceed with a cap of 100 releases and the GraphQL GithubAuthV4Service pattern once I get the go-ahead!

  11. DCjanus commented on Aug 29, 2026

    @DCjanus
    Contributor

    The original example no longer reproduces consistently. After checking several repositories, I found a more reliable reproduction case:

    This should provide a stable example for contributors to validate future fixes.

  12. DCjanus commented on Sep 13, 2026

    @DCjanus
    Contributor

    Following up on the GraphQL suggestion above, I ran some authenticated API experiments on 2026-09-13. I am posting the results here to save future contributors from repeating the same investigation.

    TL;DR: requesting only ReleaseAsset.downloadCount greatly reduces the response size for ordinary repositories, and a successful query normally costs only 1 GraphQL point. However, a single 100 releases x 100 assets query is not reliable for the high-asset repositories affected by this issue: GitHub returns RESOURCE_LIMITS_EXCEEDED partial responses or times out before returning data.

    Semantics tested

    The current REST implementation requests per_page=500, but GitHub caps the endpoint at 100 releases. It nevertheless embeds the complete assets array for each returned release; in practice that can be more than 100 assets per release.

    For this experiment I intentionally evaluated the narrower GraphQL semantics discussed above:

    GitHub's default first 100 releases, summing only the first 100 assets of each release.

    The minimal query was equivalent to:

    repository(owner: $owner, name: $repo) {
      releases(first: 100) {
        nodes {
          releaseAssets(first: 100) {
            nodes {
              downloadCount
            }
          }
        }
      }
    }

    I omitted orderBy deliberately. In a live comparison, releases(first: 100) followed the REST endpoint's default order, while explicitly using CREATED_AT DESC changed the first-page ordering.

    Results

    Repository/query Result
    photonstorm/phaser, 100 releases Success: 450 assets, no GraphQL errors, download total matched REST. REST JSON was about 4.45 MB; GraphQL JSON was about 17.5 KB. GraphQL cost was 1 and latency was about 3.6 s.
    openai/codex, 10 releases x 100 assets Success: 804 assets, cost 1, about 3.4 s.
    openai/codex, 15 releases x 100 assets Partial response with 338 RESOURCE_LIMITS_EXCEEDED errors, despite requesting only about 1,300 asset nodes.
    openai/codex, 100 releases x 100 assets Timed out after about 11.7 s without usable data. Its current REST first page contains 15,746 assets across 100 releases, with up to 173 assets in one release.
    smallprogram/OpenWrtAction, 31 releases x 100 assets Returned 3,100 asset nodes but also 2,204 RESOURCE_LIMITS_EXCEEDED errors after about 7.2 s. Its REST response currently contains 5,854 assets, with up to 226 in one release.

    Rate-limit and resource observations

    • A successful query reports rateLimit.cost: 1. This agrees with GitHub's documented primary-cost calculation.
    • The theoretical maximum here is roughly 10,100 nodes (100 releases plus 10,000 assets), well below GitHub's documented 500,000-node limit.
    • That node limit is not the binding constraint. GitHub applies additional compute/resource limits which can be reached with far fewer nodes, as the 15-release test shows.
    • GitHub also terminates GraphQL requests that take longer than about 10 seconds. In one observed timeout, the token's remaining primary allowance fell by roughly 11 points rather than the normal 1; GitHub documents that timed-out queries may incur additional deductions.
    • Partial data is not sufficient for this service. Shields' BaseGraphqlService rejects a response containing GraphQL errors, and ignoring those errors would produce an incomplete count even under the proposed 100-assets-per-release semantics.
    • Splitting this into batches of 10 releases appears technically possible, but covering 100 releases would require about 10 upstream requests and roughly 10 primary points per uncached badge response. The observed batches took about 3.4 seconds each, so this also creates substantial latency and secondary-rate-limit/CPU pressure.

    Relevant GitHub limits:

    Based on these results, GraphQL does not appear to be a suitable direction for this issue. A single query still fails on the high-asset repositories affected here, while batching would introduce too many upstream requests and too much latency.

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

    bugBugs in badges and the frontendservice-badgeNew or updated service badge

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions