download-artifact 报 intermediary 403:为什么 ListArtifacts 没有重试
按列举产物阶段、正式依赖的状态筛选与下载归属判断,受控补跑前检查外部操作。
同一工作流的并行任务下载同一份 artifact,有的成功,有的立即失败。失败日志包含 Failed to ListArtifacts、Received non-retryable error 和 Error from intermediary with HTTP status code 403。先保存完整错误,这种失败发生在列举产物阶段,尚未进入文件解压。
Failed to ListArtifacts:
Received non-retryable error:
Failed request: (403) Forbidden:
Error from intermediary with HTTP status code 403 "Forbidden"
上面为便于阅读分行展示。间歇性失败不等于已经证明限流,403 也不能单独证明仓库权限不够。
五次尝试不代表每种错误都会重试
download-artifact 使用的 artifact 客户端有多条下载路径。按 v8.0.2 锁定的 @actions/artifact 6.3.1 代码,同一运行的内部 Twirp 请求最多尝试五次,但 HTTP 状态筛选只包括 500、502、503、504 和 429。
响应正文能够正常解析、状态为 403 时,会落入不重试分支;错误正文里带有 intermediary 字样,也不会改变该版本的状态判断。网络错误、JSON 解析异常另有处理,不能概括成“任何 403 都一定只请求一次”。
因此,日志明确显示 non-retryable 时,仅等待客户端自动补齐五次请求并没有依据。应核对实际 action 引用、对应依赖和错误路径;不能因为最新版文档提到重试,就假定运行中的旧版本具有同样行为。
先核实下载对象,再决定是否补跑
保存 workflow run ID、失败任务、时间、action 引用和完整错误。对照上传任务的产物名称、ID、上传结果与保留状态,确认成功下载的并行任务确实访问了同一份产物。
若下载的是另一次运行或另一个仓库,还要检查 github-token、repository 和 run-id;该场景需要令牌具备目标仓库的 actions:read 权限。它使用的公共 API 路径与同一运行的内部客户端不同,不要把一条路径的重试规则套到另一条上。
补跑前确认任务是否会重新执行发布、迁移或其他外部操作。可以在受控验证任务中下载同一份仍有效的 artifact,并保留失败与成功时间;不要为了复查下载错误直接重跑整套部署。重复运行成功,只能说明这次下载恢复,不能反推前一次一定是服务端限流。
持续失败时,不要靠扩大权限试错
截至 2026-10-11,本文核对的正式依赖仍未包含针对 intermediary 403 的选择性重试。采用版本更新前,检查正式发行内容和实际绑定依赖,并在隔离任务验证原错误;不要把尚未合入的改动当成升级保证。
如果每次都失败,继续检查对象归属、运行令牌有效性、自托管 runner 网络路径与实际响应。分享日志时去除令牌和带凭据的地址,保留 run、job、时间与错误码以便关联。
下载错误若已经发生在解压阶段,应按归档类型处理;上传出现同名 409,则检查矩阵产物名称冲突。这些问题不会因为给 ListArtifacts 增加重试就消失。


