答案是GitHub token权限不足或配置失效导致认证失败。需检查auth.json路径及格式是否正确、token是否过期或缺少read:packages等必要权限,并确保CI中token无空格换行且正确注入。

Composer install 报错 “Could not fetch https://api.github.com/... 401 Unauthorized”
这是典型的 GitHub token 权限不足或配置失效导致的认证失败。Composer 从 2021 年起强制要求对 GitHub API 的私有/高频率请求必须携带有效 token,否则会直接 401。不是网络问题,也不是 Composer 版本太低,大概率是 auth.json 里写的 token 过期、权限缺失,或者根本没生效。
排查优先看这个文件是否存在且被正确加载:
-
auth.json默认路径是~/.composer/auth.json(Linux/macOS)或%APPDATA%\Composer\auth.json(Windows) - 项目根目录下的
auth.json只在运行composer install时临时生效,但会被全局配置覆盖(注意优先级) - 用
composer config --global --list查看是否已设置github-oauth.github.com,它和auth.json是互斥配置方式,混用容易冲突
token 权限不够:只勾了 repo,漏了 packages 或 read:packages
GitHub 新建 personal access token 时,默认权限极低。Composer 拉取私有包、访问 GitHub Packages Registry(ghcr.io)、甚至某些组织下公开但启用了 SSO 的仓库,都依赖更细粒度的 scope。
必须至少勾选:
-
read:packages—— 读取 GitHub Packages 中的包(含 public/private) -
delete:packages(可选)—— 如果要composer publish -
write:packages(可选)—— 推送包到 GHCR -
read:org—— 若私有包在组织内且启用了 SSO,缺它会 403 - 不要只勾
repo:它对 GitHub Packages 无效,且不保证跨组织访问
生成后,别手抖复制空格或换行 —— auth.json 里的 token 值必须是纯字符串,前后不能有空白符。
auth.json 格式写错或路径没生效
常见错误是结构嵌套错、引号不匹配、多写了逗号,导致 Composer 完全忽略该文件。最简能跑通的 auth.json 长这样:
{
"github-oauth": {
"github.com": "ghp_xxx..."
}
}
注意几点:
- 顶层必须是对象,不能是数组;
github-oauth是固定 key,不能写成github_oauth或githubToken - 域名必须写
github.com,不是api.github.com,也不是www.github.com - 如果同时用 GitHub Packages(
ghcr.io),需额外加gitlab.com或自定义域配置 —— 但 Composer 原生不支持ghcr.io直接认证,得靠composer config repositories.ghcr+http-basic模拟,这个容易绕晕,先确认是不是真需要它 - 改完
auth.json后,执行composer clear-cache,否则旧缓存可能掩盖问题
CI 环境下 token 被截断或注入方式不对
GitHub Actions、GitLab CI 里常把 token 当 secret 注入,但容易出两类问题:
- Secret 值末尾被自动换行或空格污染,导致 token 实际多了一个
\n—— 在 job 中加一步echo "TOKEN: '${{ secrets.GITHUB_TOKEN }}'" | cat -A看有没有$符号 - 用
composer config --global github-oauth.github.com ${{ secrets.GITHUB_TOKEN }}写入,但该命令会把 token 当作 shell 参数解析,若含特殊字符(如/、=)可能被截断 —— 更稳妥的是用echo '{...}' > ~/.composer/auth.json直接写文件 - Docker 构建中,
auth.json必须在composer install步骤前就存在于镜像内,COPY 时别漏掉.dockerignore里误删了它
本地能过但 CI 失败?八成是 token 权限或注入方式的问题,而不是 Composer 本身。











