上一篇放行了沙箱的网络出站白名单,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 摆在那不能用,迟早得正面解决。
一个把我带偏的假设
我最初的判断是这样的:
curl https://api.github.com返回 200——主机可达,curl 也接受了证书。gh偏偏报 TLS 校验失败。- 于是推断:沙箱代理对 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, andterraformmay fail TLS verification under Seatbelt. List these tools in excludedCommands to run them outside the sandbox.
把前因后果串起来就是:
- Claude Desktop 把 Claude Code 跑在 macOS 的 Seatbelt 沙箱里。
gh是 Go 程序,在 macOS 上校验证书要走系统的trustd(Security.framework),而trustd是通过 mach 服务通信的。- Seatbelt 默认挡掉了这条 mach 通道,于是
gh调不到trustd,证书没法验证,返回OSStatus -26276。 - 而
curl(LibreSSL / SecureTransport)和git(libcurl)走的是进程内的 CA bundle 校验,根本不依赖trustd——所以它俩在同一个沙箱里都没事。
这也回头解释清楚了两件事:为什么 git push 好好的而 gh 不行(一个靠 trustd、一个不靠);为什么喂 SSL_CERT_FILE 没用(问题不在 CA,在那条被挡的 mach 通道,Go 在 macOS 上本就不读这个变量)。
解法:excludedCommands
根因清楚了,解法就一行——把 gh 移出沙箱,让它恢复对 trustd 的访问。在 ~/.claude/settings.json 里加:
{
"sandbox": {
"excludedCommands": ["gh *"]
}
}
匹配规则是命令前缀,gh * 覆盖 gh pr create、gh api、gh issue list 等所有 gh 子命令。
跟上一篇放 npm run dev 一样,这个改动即时生效、不用重启会话。改完直接验证:
gh api user --jq '.login' # 返回我的用户名
gh pr list -L 3 # 正常列出 PR
全过。gh 在沙箱里彻底好了,建 PR、看 issue 直接用,curl + REST 那套兜底可以扔了。
如果以后遇到别的 Go CLI(
gcloud、terraform)在沙箱里报同样的OSStatus,往这个数组里追加"工具名 *"即可,是同一个病因。