Skip to content
返回

Claude Desktop 里 gh 报 TLS 错的原因与解法

上一篇放行了沙箱的网络出站白名单,git push 总算连上了 github。可同一次建 PR 还没完——git push 一过,gh pr create 立刻又撞上第二道墙:gh 命令报 TLS 证书校验失败。最后用的是和更早那篇同一招 sandbox.excludedCommands,但中间被一个错误假设带着绕了一大圈。记下来备查。

现象

我在 Claude Desktop 里让它走 /create-pr 建 PR,结果报错:

gh pr create ...
→ Post "https://api.github.com/graphql": tls: failed to verify certificate:
  x509: OSStatus -26276

但同一个沙箱里,git push(也是走 https://github.com)一切正常,PR 建不了纯粹卡在 gh 这一步。

临时绕过:curl + REST API

当时急着把 PR 提了,先不深究,临时用 curl 直接打 GitHub REST API 绕过去——token 用 gh auth token 取(gh 本身是登录好的,只是发不出请求):

curl -sS -X POST https://api.github.com/repos/<owner>/<repo>/pulls \
  -H "Authorization: Bearer $(gh auth token)" \
  -H "Accept: application/vnd.github+json" \
  -d '{"title":"...","head":"...","base":"main","body":"..."}'

这能成,但每次都要手拼 REST 调用,建 PR、看 issue、列 PR 全得自己来,太麻烦。gh 摆在那不能用,迟早得正面解决。

一个把我带偏的假设

我最初的判断是这样的:

  1. curl https://api.github.com 返回 200——主机可达,curl 也接受了证书。
  2. gh 偏偏报 TLS 校验失败。
  3. 于是推断:沙箱代理对 HTTPS 做了拦截 / 重签(MITM)——curl 走系统证书信任链、接受了代理重签的证书;而 gh 是 Go 程序、用自己那套证书验证逻辑,不信任代理注入的中间人 CA,所以报错。

听起来很顺,方向也就顺着这个假设走了:要么让 gh 信任代理的 CA,要么让代理对 api.github.com 改成 TLS 直通。

这个假设是错的。 还好动手改之前先去验了一下,没直接照着错方向折腾。

真正的根因:Seatbelt 挡了 trustd

查了下 OSStatus -26276 这个错误码,线索就清楚了。官方文档的沙箱排错一节里直接写着这条:

Go-based CLIs fail TLS verification on macOS: tools such as gh, gcloud, and terraform may fail TLS verification under Seatbelt. List these tools in excludedCommands to run them outside the sandbox.

把前因后果串起来就是:

这也回头解释清楚了两件事:为什么 git push 好好的而 gh 不行(一个靠 trustd、一个不靠);为什么喂 SSL_CERT_FILE 没用(问题不在 CA,在那条被挡的 mach 通道,Go 在 macOS 上本就不读这个变量)。

解法:excludedCommands

根因清楚了,解法就一行——把 gh 移出沙箱,让它恢复对 trustd 的访问。在 ~/.claude/settings.json 里加:

{
  "sandbox": {
    "excludedCommands": ["gh *"]
  }
}

匹配规则是命令前缀,gh * 覆盖 gh pr creategh apigh issue list 等所有 gh 子命令。

跟上一篇放 npm run dev 一样,这个改动即时生效、不用重启会话。改完直接验证:

gh api user --jq '.login'   # 返回我的用户名
gh pr list -L 3             # 正常列出 PR

全过。gh 在沙箱里彻底好了,建 PR、看 issue 直接用,curl + REST 那套兜底可以扔了。

如果以后遇到别的 Go CLI(gcloudterraform)在沙箱里报同样的 OSStatus,往这个数组里追加 "工具名 *" 即可,是同一个病因。


分享这篇文章:

下一篇
Claude Desktop 沙箱挡住了 github.com