触发条件

  • 在 GitHub Actions 里用自带的 secrets.GITHUB_TOKEN 调 GraphQL 的 user.contributionsCollection。
  • 典型场景:自建 profile README 的贡献日历 / 统计卡片,想走官方接口拿数据。

症状

  • 请求返回 HTTP 200,但 body 里有:

    {"errors": [{"message": "Resource not accessible by integration"}]}
  • 在 workflow 里写 permissions: write-all 完全没用。

根因

GITHUB_TOKEN 不是"你的令牌"。它是 GitHub App 的安装访问令牌 (installation access token):

  • 它的权限边界 = 令牌所绑定的那一个仓库。
  • 而 contributionsCollection 是 用户级 资源:它要汇总这个人在所有仓库(含私有)的活动,天然跨出单个仓库的边界。

用户级数据不发给 App 安装令牌,所以被拒。跟 permissions 无关——那个开关管的是仓库内读写权限,跨不过仓库边界。

一句话判据:字段需要跨仓库聚合 → 用户级 → GITHUB_TOKEN 拿不到。

实测数据(2026-09-24)

同一账号、同一时刻,两个来源对比:

来源一年贡献数
公开贡献页(匿名访问)606
GraphQL + 带 repo 权限的 PAT1003

差的 397 全部在私有仓库里。第三方公开统计服务读的是上面那个公开页,所以只能看到 606。

三条可行路线

1. PAT(推荐)

  • classic token 勾 read:user + repo,存成仓库 secret,workflow 里用 env: 注入。
  • read:user 只够公开贡献;要把私有仓库的贡献也算进来必须加 repo。
  • 官方接口,最稳,唯一能拿到私有贡献的路子。

2. 抓公开网页

  • GET https://github.com/users/<user>/contributions,匿名可读,不需要任何令牌。
  • HTML 结构:总数在 <h2 id="js-contribution-activity-description"> 里,每日次数在 <tool-tip> 里。
  • 代价:GitHub 改版就失效,且只有公开贡献。

3. 只用 REST

  • /users/<user> 的 public_repos、followers 等,GITHUB_TOKEN 完全够用(5000 次/小时)。
  • 完全不碰贡献日历,最不容易坏。

验证方法

拿匿名页面当对照,和自己的数字比:

curl -sS -H "User-Agent: Mozilla/5.0" \
  "https://github.com/users/<user>/contributions" \
  | grep -oE '[0-9]+ contribution' | awk '{s+=$1} END {print s+0}'

对不上,差额就是私有仓库那部分——证明 PAT 确实多读到了东西。

下次如何识别

  • GitHub API 报 Resource not accessible by integration → 直接判定:拿 App 安装令牌碰了用户级资源。加 permissions 是死路,别再试。
  • 换令牌前先想清楚:GITHUB_TOKEN 解决"当前仓库内"的操作;PAT 解决"以我的身份,跨仓库"的操作。

遇到场景

2026-09-24,给 connectedGraph/connectedGraph 做自建 GitHub Stats(Actions 定时抓数据 → 生成 SVG → 提交回仓库),替换掉原本的第三方 Vercel 公共实例。REST 部分 GITHUB_TOKEN 一路顺畅,只有贡献日历这一项必须换 PAT。

相关概念


本文归档于个人知识库 · 领域:CS & AI · 模块:04 Project_CS_AI/General