Skip to content

gh run download doesn't download latest artifact from latest attempt #12437

Open

Description

@adangel

Describe the bug

When you have a workflow run, that creates an artifact and you rerun this workflow, then you have two artifacts (2 attempts).

Using gh run download <run-id> --name artifact-name, the artifact from the first attempt is downloaded, which is not the latest artifact.

Affected version

2.83.2

Steps to reproduce the behavior

  1. Type this 'GH_DEBUG=api gh run download 20775770442 --repo pmd/pmd --name pmd-regression-tester'
  2. View the output, it downloads the artifact https://api.github.com/repos/pmd/pmd/actions/artifacts/5047083281/zip - which is the artifacts from the first attempt of the workflow run

Expected vs actual behavior

Expected: It should download the artifact from the second attempt of the workflow run: https://api.github.com/repos/pmd/pmd/actions/artifacts/5047215209/zip

Actual: It downloaded the artifact from the first attempt of the workflow run: https://api.github.com/repos/pmd/pmd/actions/artifacts/5047083281/zip

Workaround

Instead of the "gh run download" command, you can use this:

download_url=$(gh api \
  "/repos/$OWNER/$REPO/actions/runs/$RUN_ID/artifacts?name=$ARTIFACT_NAME" \
  --jq '.artifacts|sort_by(.created_at)|.[-1]|.archive_download_url' \
)

curl -L \
  -H "Authorization: Bearer $GH_TOKEN" \
  --output artifact.zip \
  $download_url
unzip artifact.zip
rm artifact.zip

This sorts the artifacts by "created_at" timestamp and chooses the last one.

Logs

Details
GH_DEBUG=api gh run download 20775770442 --repo pmd/pmd --name pmd-regression-tester
* Request at 2026-01-07 13:30:01.253226536 +0100 CET m=+0.169792185
* Request to https://api.github.com/repos/pmd/pmd/actions/runs/20775770442/artifacts?per_page=100
> GET /repos/pmd/pmd/actions/runs/20775770442/artifacts?per_page=100 HTTP/1.1
> Host: api.github.com
> Accept: application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview
> Authorization: token ...
> Content-Type: application/json; charset=utf-8
> Time-Zone: Europe/Berlin
> User-Agent: GitHub CLI 2.83.2

⣻< HTTP/2.0 200 OK
< Access-Control-Allow-Origin: *
< Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset
< Cache-Control: private, max-age=60, s-maxage=60
< Content-Security-Policy: default-src 'none'
< Content-Type: application/json; charset=utf-8
< Date: Wed, 07 Jan 2026 12:30:01 GMT
< Etag: W/"e88a52f7ab5fa4e26bd7b1823d72705aafe994d79ef35945a9996fc28c99226f"
< Github-Authentication-Token-Expiration: 2026-02-09 09:54:11 +0100
< Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin
< Server: github.com
< Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
< Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With
< X-Accepted-Github-Permissions: actions=read
< X-Content-Type-Options: nosniff
< X-Frame-Options: deny
< X-Github-Api-Version-Selected: 2022-11-28
< X-Github-Media-Type: github.v3; param=merge-info-preview.nebula-preview; format=json
< X-Github-Request-Id: EB1F:1776B2:4392FC6:3D33E34:695E51C9
< X-Ratelimit-Limit: 5000
< X-Ratelimit-Remaining: 4999
< X-Ratelimit-Reset: 1767792601
< X-Ratelimit-Resource: core
< X-Ratelimit-Used: 1
< X-Xss-Protection: 0

{
  "total_count": 7,
  "artifacts": [
    {
      "id": 5046971645,
      "node_id": "MDg6QXJ0aWZhY3Q1MDQ2OTcxNjQ1",
      "name": "compile-artifact",
      "size_in_bytes": 31712152,
      "url": "https://api.github.com/repos/pmd/pmd/actions/artifacts/5046971645",
      "archive_download_url": "https://api.github.com/repos/pmd/pmd/actions/artifacts/5046971645/zip",
      "expired": false,
      "digest": "sha256:7f060bcd4173aee923a2f18cef32575660485b8a1a0df98a0235d7de2f412068",
      "created_at": "2026-01-07T08:53:09Z",
      "updated_at": "2026-01-07T08:53:09Z",
      "expires_at": "2026-04-07T08:49:42Z",
      "workflow_run": {
        "id": 20775770442,
        "repository_id": 4992906,
        "head_repository_id": 4992906,
        "head_branch": "dependabot/maven/scalameta.version-4.14.4",
        "head_sha": "c74a38eec86530a91dae98dc3de5b61ae556aa55"
      }
    },
    {
      "id": 5046971843,
      "node_id": "MDg6QXJ0aWZhY3Q1MDQ2OTcxODQz",
      "name": "staging-repository",
      "size_in_bytes": 8364820,
      "url": "https://api.github.com/repos/pmd/pmd/actions/artifacts/5046971843",
      "archive_download_url": "https://api.github.com/repos/pmd/pmd/actions/artifacts/5046971843/zip",
      "expired": false,
      "digest": "sha256:c88e407a82262308fd1cb121e4abdf6b181e6d04839e2d90f0ed5fba1dc9fb52",
      "created_at": "2026-01-07T08:53:11Z",
      "updated_at": "2026-01-07T08:53:11Z",
      "expires_at": "2026-04-07T08:49:42Z",
      "workflow_run": {
        "id": 20775770442,
        "repository_id": 4992906,
        "head_repository_id": 4992906,
        "head_branch": "dependabot/maven/scalameta.version-4.14.4",
        "head_sha": "c74a38eec86530a91dae98dc3de5b61ae556aa55"
      }
    },
    {
      "id": 5046972269,
      "node_id": "MDg6QXJ0aWZhY3Q1MDQ2OTcyMjY5",
      "name": "dist-artifact",
      "size_in_bytes": 86209640,
      "url": "https://api.github.com/repos/pmd/pmd/actions/artifacts/5046972269",
      "archive_download_url": "https://api.github.com/repos/pmd/pmd/actions/artifacts/5046972269/zip",
      "expired": false,
      "digest": "sha256:7f542b5ca1021f0934cea0cdb451bd10a1d48b1ee99f3b90ef83fbcd4bfbbc19",
      "created_at": "2026-01-07T08:53:14Z",
      "updated_at": "2026-01-07T08:53:14Z",
      "expires_at": "2026-04-07T08:49:42Z",
      "workflow_run": {
        "id": 20775770442,
        "repository_id": 4992906,
        "head_repository_id": 4992906,
        "head_branch": "dependabot/maven/scalameta.version-4.14.4",
        "head_sha": "c74a38eec86530a91dae98dc3de5b61ae556aa55"
      }
    },
    {
      "id": 5046997887,
      "node_id": "MDg6QXJ0aWZhY3Q1MDQ2OTk3ODg3",
      "name": "docs-artifact",
      "size_in_bytes": 9531535,
      "url": "https://api.github.com/repos/pmd/pmd/actions/artifacts/5046997887",
      "archive_download_url": "https://api.github.com/repos/pmd/pmd/actions/artifacts/5046997887/zip",
      "expired": false,
      "digest": "sha256:303f44e45e3bad8245d8a30fa12ac2a71e07ce7e2e23cfa4229cd4bf7e34688d",
      "created_at": "2026-01-07T08:56:04Z",
      "updated_at": "2026-01-07T08:56:04Z",
      "expires_at": "2026-04-07T08:49:42Z",
      "workflow_run": {
        "id": 20775770442,
        "repository_id": 4992906,
        "head_repository_id": 4992906,
        "head_branch": "dependabot/maven/scalameta.version-4.14.4",
        "head_sha": "c74a38eec86530a91dae98dc3de5b61ae556aa55"
      }
    },
    {
      "id": 5047033823,
      "node_id": "MDg6QXJ0aWZhY3Q1MDQ3MDMzODIz",
      "name": "javadocs-artifact",
      "size_in_bytes": 19882752,
      "url": "https://api.github.com/repos/pmd/pmd/actions/artifacts/5047033823",
      "archive_download_url": "https://api.github.com/repos/pmd/pmd/actions/artifacts/5047033823/zip",
      "expired": false,
      "digest": "sha256:7cf6b24f3e0d3a6bc01736f42949b3c17e766229869f0cffa70a06c4742c5153",
      "created_at": "2026-01-07T09:00:09Z",
      "updated_at": "2026-01-07T09:00:09Z",
      "expires_at": "2026-04-07T08:49:42Z",
      "workflow_run": {
        "id": 20775770442,
        "repository_id": 4992906,
        "head_repository_id": 4992906,
        "head_branch": "dependabot/maven/scalameta.version-4.14.4",
        "head_sha": "c74a38eec86530a91dae98dc3de5b61ae556aa55"
      }
    },
    {
      "id": 5047083281,
      "node_id": "MDg6QXJ0aWZhY3Q1MDQ3MDgzMjgx",
      "name": "pmd-regression-tester",
      "size_in_bytes": 348,
      "url": "https://api.github.com/repos/pmd/pmd/actions/artifacts/5047083281",
      "archive_download_url": "https://api.github.com/repos/pmd/pmd/actions/artifacts/5047083281/zip",
      "expired": false,
      "digest": "sha256:a88bf576727865790739e17380b7c5447cedb376876f35b8462982be2a5ecca8",
      "created_at": "2026-01-07T09:05:47Z",
      "updated_at": "2026-01-07T09:05:47Z",
      "expires_at": "2026-04-07T08:49:42Z",
      "workflow_run": {
        "id": 20775770442,
        "repository_id": 4992906,
        "head_repository_id": 4992906,
        "head_branch": "dependabot/maven/scalameta.version-4.14.4",
        "head_sha": "c74a38eec86530a91dae98dc3de5b61ae556aa55"
      }
    },
    {
      "id": 5047215209,
      "node_id": "MDg6QXJ0aWZhY3Q1MDQ3MjE1MjA5",
      "name": "pmd-regression-tester",
      "size_in_bytes": 312,
      "url": "https://api.github.com/repos/pmd/pmd/actions/artifacts/5047215209",
      "archive_download_url": "https://api.github.com/repos/pmd/pmd/actions/artifacts/5047215209/zip",
      "expired": false,
      "digest": "sha256:2a5dc18badea9c17f6d7a33573b56784609b3e357369084aba06f7a105ecbec4",
      "created_at": "2026-01-07T09:19:23Z",
      "updated_at": "2026-01-07T09:19:23Z",
      "expires_at": "2026-04-07T09:18:47Z",
      "workflow_run": {
        "id": 20775770442,
        "repository_id": 4992906,
        "head_repository_id": 4992906,
        "head_branch": "dependabot/maven/scalameta.version-4.14.4",
        "head_sha": "c74a38eec86530a91dae98dc3de5b61ae556aa55"
      }
    }
  ]
}

* Request took 315.646026ms
* Request at 2026-01-07 13:30:01.570352359 +0100 CET m=+0.486917998
* Request to https://api.github.com/repos/pmd/pmd/actions/artifacts/5047083281/zip
> GET /repos/pmd/pmd/actions/artifacts/5047083281/zip HTTP/1.1
> Host: api.github.com
> Accept: application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview
> Authorization: token ...
> Content-Type: application/json; charset=utf-8
> Time-Zone: Europe/Berlin
> User-Agent: GitHub CLI 2.83.2

⣻< HTTP/2.0 302 Found
< Access-Control-Allow-Origin: *
< Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset
< Content-Length: 0
< Content-Security-Policy: default-src 'none'
< Content-Type: text/html;charset=utf-8
< Date: Wed, 07 Jan 2026 12:30:01 GMT
< Location: https://productionresultssa8.blob.core.windows.net/actions-results/f7e160b1-337d-43e1-9a06-e582cc2049a5/workflow-job-run-85f2ffda-a5d8-59a2-b022-2720c75e89c1/artifacts/320b47470b5efc53d6aabbf132e1c041d7dad8aa50ab2cb6ef7bfe2f874c8b3a.zip?rscd=attachment%3B+filename%3D%22pmd-regression-tester.zip%22&se=2026-01-07T12%3A40%3A01Z&sig=1HHFeiqosmSuXP3o94EESFojnJrnTinXYuQvt9arxGk%3D&ske=2026-01-07T21%3A18%3A57Z&skoid=ca7593d4-ee42-46cd-af88-8b886a2f84eb&sks=b&skt=2026-01-07T09%3A18%3A57Z&sktid=398a6654-997b-47e9-b12b-9515b896b4de&skv=2025-11-05&sp=r&spr=https&sr=b&st=2026-01-07T12%3A29%3A56Z&sv=2025-11-05
< Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin
< Server: github.com
< Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
< Vary: Accept-Encoding, Accept, X-Requested-With
< X-Content-Type-Options: nosniff
< X-Frame-Options: deny
< X-Github-Api-Version-Selected: 2022-11-28
< X-Github-Request-Id: EB1F:1776B2:439318C:3D33FF8:695E51C9
< X-Ratelimit-Limit: 5000
< X-Ratelimit-Remaining: 4998
< X-Ratelimit-Reset: 1767792601
< X-Ratelimit-Resource: core
< X-Ratelimit-Used: 2
< X-Xss-Protection: 0

* Request took 299.224323ms
* Request at 2026-01-07 13:30:01.869640176 +0100 CET m=+0.786205809
* Request to https://productionresultssa8.blob.core.windows.net/actions-results/f7e160b1-337d-43e1-9a06-e582cc2049a5/workflow-job-run-85f2ffda-a5d8-59a2-b022-2720c75e89c1/artifacts/320b47470b5efc53d6aabbf132e1c041d7dad8aa50ab2cb6ef7bfe2f874c8b3a.zip?rscd=attachment%3B+filename%3D%22pmd-regression-tester.zip%22&se=2026-01-07T12%3A40%3A01Z&sig=1HHFeiqosmSuXP3o94EESFojnJrnTinXYuQvt9arxGk%3D&ske=2026-01-07T21%3A18%3A57Z&skoid=ca7593d4-ee42-46cd-af88-8b886a2f84eb&sks=b&skt=2026-01-07T09%3A18%3A57Z&sktid=398a6654-997b-47e9-b12b-9515b896b4de&skv=2025-11-05&sp=r&spr=https&sr=b&st=2026-01-07T12%3A29%3A56Z&sv=2025-11-05
> GET /actions-results/f7e160b1-337d-43e1-9a06-e582cc2049a5/workflow-job-run-85f2ffda-a5d8-59a2-b022-2720c75e89c1/artifacts/320b47470b5efc53d6aabbf132e1c041d7dad8aa50ab2cb6ef7bfe2f874c8b3a.zip?rscd=attachment%3B+filename%3D%22pmd-regression-tester.zip%22&se=2026-01-07T12%3A40%3A01Z&sig=1HHFeiqosmSuXP3o94EESFojnJrnTinXYuQvt9arxGk%3D&ske=2026-01-07T21%3A18%3A57Z&skoid=ca7593d4-ee42-46cd-af88-8b886a2f84eb&sks=b&skt=2026-01-07T09%3A18%3A57Z&sktid=398a6654-997b-47e9-b12b-9515b896b4de&skv=2025-11-05&sp=r&spr=https&sr=b&st=2026-01-07T12%3A29%3A56Z&sv=2025-11-05 
> Host: productionresultssa8.blob.core.windows.net
> Accept: application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview
> Content-Type: application/json; charset=utf-8
> Referer: https://api.github.com/repos/pmd/pmd/actions/artifacts/5047083281/zip
> Time-Zone: Europe/Berlin
> User-Agent: GitHub CLI 2.83.2

⣯< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Access-Control-Allow-Origin: *
< Access-Control-Expose-Headers: x-ms-request-id,Server,x-ms-version,Content-Type,ETag,Last-Modified,x-ms-creation-time,x-ms-lease-status,x-ms-lease-state,x-ms-blob-type,Content-Disposition,x-ms-server-encrypted,Accept-Ranges,Content-Length,Date,Transfer-Encoding
< Content-Disposition: attachment; filename="pmd-regression-tester.zip"
< Content-Length: 348
< Content-Type: zip
< Date: Wed, 07 Jan 2026 12:30:02 GMT
< Etag: "0x8DE4DCBF23E85C6"
< Last-Modified: Wed, 07 Jan 2026 09:05:47 GMT
< Server: Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0
< X-Ms-Blob-Type: BlockBlob
< X-Ms-Creation-Time: Wed, 07 Jan 2026 09:05:47 GMT
< X-Ms-Lease-State: available
< X-Ms-Lease-Status: unlocked
< X-Ms-Request-Id: 1ce1129d-b01e-00ce-15d1-7f36b3000000
< X-Ms-Server-Encrypted: true
< X-Ms-Version: 2025-11-05

* Request took 455.557872ms

Activity

  1. jsoref commented on Jan 7, 2026

    @jsoref
    Contributor

    I came here to file this. My guess is that github changed the sort order of the endpoint.

    Note that the workaround will almost certainly fail if you do enough reruns to trigger pagination, or if your jobs generate lots of artifacts.

  2. jsoref commented on Jan 7, 2026

    @jsoref
    Contributor

    Fwiw, afaict the array limit is 30 which means that if you have more than 30 artifacts in the results, you're going to hit pagination:

    $ gh api '/repos/check-spelling-sandbox/MeshCore/actions/runs/20750207409/artifacts?name=check-spelling-comment' > x &&
       jq '.total_count' x &&
       jq '.artifacts |length' x
    295
    30
  3. jsoref commented on Jan 7, 2026

    @jsoref
    Contributor

    In fact, it's much better (for my use case at least) to ask for per_page=1:

    jsoref@jsoref-mbp ~ % GH_DEBUG=api gh api 'https://api.github.com/repositories/1128664294/actions/runs/20750207409/artifacts?name=check-spelling-comment&per_page=1' > a
    
    * Request at 2026-01-07 13:14:46.65526 -0500 EST m=+0.058269001
    * Request to https://api.github.com/repositories/1128664294/actions/runs/20750207409/artifacts?name=check-spelling-comment&per_page=1
    > GET /repositories/1128664294/actions/runs/20750207409/artifacts?name=check-spelling-comment&per_page=1 HTTP/1.1
    > Host: api.github.com
    > Accept: */*
    > Authorization: token ████████████████████
    > Content-Type: application/json; charset=utf-8
    > Time-Zone: America/Toronto
    > User-Agent: GitHub CLI 2.83.2
    
    < HTTP/2.0 200 OK
    < Access-Control-Allow-Origin: *
    < Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset
    < Cache-Control: private, max-age=60, s-maxage=60
    < Content-Security-Policy: default-src 'none'
    < Content-Type: application/json; charset=utf-8
    < Date: Wed, 07 Jan 2026 18:14:47 GMT
    < Etag: W/"2cbcbafa466e17b74d3f809c2472ba508ade0a0e742ee2340a1c97d6fe3850fd"
    < Link: <https://api.github.com/repositories/1128664294/actions/runs/20750207409/artifacts?name=check-spelling-comment&per_page=1&page=2>; rel="next", <https://api.github.com/repositories/1128664294/actions/runs/20750207409/artifacts?name=check-spelling-comment&per_page=1&page=333>; rel="last"
    < Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin
    < Server: github.com
    < Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
    < Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With
    < X-Accepted-Oauth-Scopes:
    < X-Content-Type-Options: nosniff
    < X-Frame-Options: deny
    < X-Github-Api-Version-Selected: 2022-11-28
    < X-Github-Media-Type: github.v3; format=json
    < X-Github-Request-Id: C8AF:25C17B:1BEF5F5:79DD94A:695EA296
    < X-Oauth-Client-Id: 178c6fc778ccc68e1d6a
    < X-Oauth-Scopes: gist, read:org, repo, workflow, write:packages
    < X-Ratelimit-Limit: 5000
    < X-Ratelimit-Remaining: 4635
    < X-Ratelimit-Reset: 1767810296
    < X-Ratelimit-Resource: core
    < X-Ratelimit-Used: 365
    < X-Xss-Protection: 0
    
    {
      "total_count": 333,
      "artifacts": [
        {
          "id": 5037147613,
          "node_id": "MDg6QXJ0aWZhY3Q1MDM3MTQ3NjEz",
          "name": "check-spelling-comment",
          "size_in_bytes": 46486,
          "url": "https://api.github.com/repos/check-spelling-sandbox/MeshCore/actions/artifacts/5037147613",
          "archive_download_url": "https://api.github.com/repos/check-spelling-sandbox/MeshCore/actions/artifacts/5037147613/zip",
          "expired": false,
          "digest": "sha256:a8c226d11d54f9507d24394fceb5e02b9ea2ccd27f615e1d706884527101e2eb",
          "created_at": "2026-01-06T13:47:12Z",
          "updated_at": "2026-01-06T13:47:12Z",
          "expires_at": "2026-04-06T13:46:23Z",
          "workflow_run": {
            "id": 20750207409,
            "repository_id": 1128664294,
            "head_repository_id": 1128664294,
            "head_branch": "spell-check-with-spelling",
            "head_sha": "5aefd5dd6ed4512a2e6b2b4470988bbe06c0319c"
          }
        }
      ]
    }
    * Request took 273.9795ms
    

    And then the last (from the Link: header) page:

    jsoref@jsoref-mbp ~ % GH_DEBUG=api gh api 'https://api.github.com/repositories/1128664294/actions/runs/20750207409/artifacts?name=check-spelling-comment&per_page=1&page=333' > b
    * Request at 2026-01-07 13:14:58.527194 -0500 EST m=+0.088069542
    * Request to https://api.github.com/repositories/1128664294/actions/runs/20750207409/artifacts?name=check-spelling-comment&per_page=1&page=333
    > GET /repositories/1128664294/actions/runs/20750207409/artifacts?name=check-spelling-comment&per_page=1&page=333 HTTP/1.1
    > Host: api.github.com
    > Accept: */*
    > Authorization: token ████████████████████
    > Content-Type: application/json; charset=utf-8
    > Time-Zone: America/Toronto
    > User-Agent: GitHub CLI 2.83.2
    
    < HTTP/2.0 200 OK
    < Access-Control-Allow-Origin: *
    < Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset
    < Cache-Control: private, max-age=60, s-maxage=60
    < Content-Security-Policy: default-src 'none'
    < Content-Type: application/json; charset=utf-8
    < Date: Wed, 07 Jan 2026 18:14:58 GMT
    < Etag: W/"670f2b028d03d5ba78caa3a6e3b6358f91c634a06e0382737b5f7be408101970"
    < Link: <https://api.github.com/repositories/1128664294/actions/runs/20750207409/artifacts?name=check-spelling-comment&per_page=1&page=332>; rel="prev", <https://api.github.com/repositories/1128664294/actions/runs/20750207409/artifacts?name=check-spelling-comment&per_page=1&page=1>; rel="first"
    < Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin
    < Server: github.com
    < Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
    < Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With
    < X-Accepted-Oauth-Scopes:
    < X-Content-Type-Options: nosniff
    < X-Frame-Options: deny
    < X-Github-Api-Version-Selected: 2022-11-28
    < X-Github-Media-Type: github.v3; format=json
    < X-Github-Request-Id: C8EA:3B20D0:1C4FDF4:7AFF470:695EA2A2
    < X-Oauth-Client-Id: 178c6fc778ccc68e1d6a
    < X-Oauth-Scopes: gist, read:org, repo, workflow, write:packages
    < X-Ratelimit-Limit: 5000
    < X-Ratelimit-Remaining: 4634
    < X-Ratelimit-Reset: 1767810296
    < X-Ratelimit-Resource: core
    < X-Ratelimit-Used: 366
    < X-Xss-Protection: 0
    
    {
      "total_count": 333,
      "artifacts": [
        {
          "id": 5053199227,
          "node_id": "MDg6QXJ0aWZhY3Q1MDUzMTk5MjI3",
          "name": "check-spelling-comment",
          "size_in_bytes": 11047,
          "url": "https://api.github.com/repos/check-spelling-sandbox/MeshCore/actions/artifacts/5053199227",
          "archive_download_url": "https://api.github.com/repos/check-spelling-sandbox/MeshCore/actions/artifacts/5053199227/zip",
          "expired": false,
          "digest": "sha256:61311ca3cbfee5254a13209aee4b5f448d18c7b6e6edc962fd810c43d53a78c6",
          "created_at": "2026-01-07T18:09:59Z",
          "updated_at": "2026-01-07T18:09:59Z",
          "expires_at": "2026-04-07T18:09:35Z",
          "workflow_run": {
            "id": 20750207409,
            "repository_id": 1128664294,
            "head_repository_id": 1128664294,
            "head_branch": "spell-check-with-spelling",
            "head_sha": "5aefd5dd6ed4512a2e6b2b4470988bbe06c0319c"
          }
        }
      ]
    }
    * Request took 227.117792ms
    
  4. adangel commented on Jan 8, 2026

    @adangel
    Author

    Yes, if you have many repeated runs of your workflow (aka attempts), then you might hit the paging problem. In order to sort the artifacts client-side, you need to have them all. I see, you have re-run your workflow 334 times...

    Note, that you can only upload an artifact with a specific name once ("it is no longer possible to upload to the same named Artifact multiple times" -> https://github.com/actions/upload-artifact?tab=readme-ov-file#breaking-changes). The only way to have multiple artifacts with the same name seems to be now when you re-run a workflow.

    For my use case, I don't rerun the workflows that often, so the default page size of 30 is ok. You could try to query a single very big page (e.g. per_page=1000), but there's probably also an undocumented upper limit. Your approach using per_page=1 (which is for one named artifact exactly one workflow run attempt) and using the "last" link, sound reasonable. However, you'll depend on the sort order, that github implements. If the last is not the latest, then this won't work.

  5. babakks commented on Jan 8, 2026

    @babakks
    Member

    Thanks for the investigation here, @adangel and @jsoref! 🙏

    I'm curious what would you get if you omit the run ID, @adangel. 🤔

    Can you please run this and share the output (redact where needed)?

    GH_DEBUG=api gh run download --repo pmd/pmd --name pmd-regression-tester

    The reason I'm asking this is that when the run ID is missing, gh uses a different API endpoint, and I'm curious to see how that endpoint sort the entries.

  6. added
    more-info-neededMore info needed from user/contributor
    gh-runrelating to the gh run command
    on Jan 8, 2026
  7. jsoref commented on Jan 8, 2026

    @jsoref
    Contributor

    Fwiw, I've now replaced my use of gh run download.

    Of course, I now will need to rely on the endpoint sort order being oldest to newest and I'm hoping to ship with this commit shortly (and I release once or twice a year).

  8. babakks commented on Jan 8, 2026

    @babakks
    Member

    My bad, nevermind. You folks have already checked that route. 🏅

    Seems like gh doesn't expect to see colliding artifact names in the same run. But it's possible with retry attempts.

    However, I have to check internally to see if there has been any changes to the endpoint that returns the list of artifacts for a single run.

  9. added and removed
    more-info-neededMore info needed from user/contributor
    on Jan 8, 2026
  10. jsoref commented on Jan 8, 2026

    @jsoref
    Contributor

    (I might add a check to my code to ensure it picks the newest between the first and the last page in the case that github changes the sort order. I'll talk w/ @nebuk89 about this today -- didn't happen)

  11. jsoref commented on Jan 8, 2026

    @jsoref
    Contributor

    Fwiw, the api appears to say the max page size is 100:

    per_page integer
    The number of results per page (max 100). For more information, see "Using pagination in the REST API."

    Default: 30

    ... Conveniently I've already far exceeded that. -- I was planning to stop closer to 1000, but it was a slow process and I stopped and determined I'd already proved my point and didn't feel a need to continue.

    For a different, but related endpoint, the oldest item is on the last page:
    https://github.com/check-spelling/check-spelling/blame/0b27d64c0ff97ddad604b38de45a92e722637dbf/t/apply.t#L77-L99

    And, yes, I've added some code to my gh-run-download to check to see which is newer (first or last) and favor the new one. I'm also fixed apply.pl to properly get the latest version for reruns.

    Fwiw, while there's a last header in the examples above, I had some trouble dealing w/ that header in apply.pl (it looked like I ran into a case where that header wasn't present), so for that, I just guess that i can get to the nth object by going to the nth page of 1 per page pages -- it saves some parsing as I already have to parse the json of the first page to get the object on that page. And, of course, if github decides to make the list randomly sorted, then, I give up.

    I also ran into some fun cases where GitHub's servers sent out pointers to domains that have been out of use for over a decade.

    Note that I run regression testing on apply.pl daily, so if other behaviors change in this stuff, I should be notified. I'm tempted to add a test to assert the order of reruns, but, that's actually pretty annoying to do. I think I'd need to have a workflow that runs (probably on a schedule) that uploads an artifact, and then have a workflow file with on:/workflow_run:/completed for that workflow which in turn triggers a rerun for that workflow (but only once).

  12. 4 remaining items

  13. changed the title [-]gh run download doesn't download latest artifact[/-] [+]`gh run download` doesn't download latest artifact[/+] on Mar 9, 2026
  14. changed the title [-]`gh run download` doesn't download latest artifact[/-] [+]`gh run download` doesn't download latest artifact from latest attempt[/+] on Mar 9, 2026
  15. added
    enhancementa request to improve CLI
    and removed
    bugSomething isn't working
    on Mar 9, 2026
  16. github-actions commented on Mar 9, 2026

    @github-actions
    Contributor

    Thank you for your issue! We have categorized it as an enhancement (feature or improvement) request, and it has been added to our backlog. In doing so, we are not committing to implementing this feature at this time, but, we will consider it for future releases based on community feedback and our own product roadmap.

    Unless you see the help wanted Contributions welcome label, we are not currently looking for external contributions for this feature.

    If you come across this issue and would like to see it implemented, please add a thumbs up! This will help us prioritize the feature. Please only comment if you have additional information or viewpoints to contribute.

  17. added
    coreThis issue is not accepting PRs from outside contributors
    on Mar 9, 2026
  18. williammartin commented on Mar 10, 2026

    @williammartin
    Member

    https://docs.github.com/en/rest/actions/artifacts?apiVersion=2022-11-28#list-workflow-run-artifacts now contains a direction query parameter. Note that we currently support the 4 most recent GHES versions, so this won't be a complete solution for now.

  19. jsoref commented on Mar 10, 2026

    @jsoref
    Contributor

    @williammartin is there a way to determine whether the direction parameter was honored? If so, that'd ensure that for the FPT instance, it's one query + one retrieval. If not, I think it's still two queries + one retrieval.

  20. williammartin commented on Mar 10, 2026

    @williammartin
    Member

    @williammartin is there a way to determine whether the direction parameter was honored? If so, that'd ensure that for the FPT instance, it's one query + one retrieval. If not, I think it's still two queries + one retrieval.

    I don't think the API will tell us this, so we can either do a heuristic check, or probably add something to the feature detector to do a version check based on the host being targeted (if GHES). Some prior art:

    const (
    // enterpriseAdvancedIssueSearchSupport is the minimum version of GHES that
    // supports advanced issue search and gh should use it.
    //
    // Note that advanced issue search is also available on GHES 3.17, but it's
    // at the preview stage and is not as mature as it is on github.com or later
    // GHES version.
    enterpriseAdvancedIssueSearchSupport = "3.18.0"
    )

  21. jsoref commented on Mar 10, 2026

    @jsoref
    Contributor

    That appears to require a trip to /meta. I don't think it's worth it. It's simpler to just send a query for first + last and compare. Once GHES supports direction, the code to get last + compare can be dropped.

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

    coreThis issue is not accepting PRs from outside contributorsenhancementa request to improve CLIgh-runrelating to the gh run commandstale

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions