现象
Hobby计划下,使用 vercel 命令行部署时候出现: Error: Unexpected error. Please try again later. ()
vercel --prod
🔍 Inspect: https://vercel.com/xx-projects/xx-site/xx [4s]
⏳ Preview: https://xx-site-xx-projects.vercel.app [4s]
Error: Unexpected error. Please try again later. ()输出debug信息, vercel --prod --debug,出现类似错误:
[client-debug] 2026-03-16T06:25:05.466Z Waiting for builds and the deployment to complete...
> [debug] [2026-03-16T06:25:05.666Z] #4 ← 200 OK: sfo1::9z6ng-1773642305713-c49af82dc718 [201ms]
> [debug] [2026-03-16T06:25:05.815Z] #5 → GET https://api.vercel.com/v3/now/deployments/xxx
[client-debug] 2026-03-16T06:25:06.111Z Yielding a 'error' event
> [debug] [2026-03-16T06:25:06.113Z] Retrying: AbortError: The user aborted a request.
AbortError: The user aborted a request.
at abort2 (file:///xx/pnpm/global/5/.pnpm/vercel@50.32.5_typescript@5.9.3/node_modules/vercel/dist/chunks/chunk-QXRJ52T4.js:2796:23)
at AbortSignal.abortAndFinalize2 (file:///xx/pnpm/global/5/.pnpm/vercel@50.32.5_typescript@5.9.3/node_modules/vercel/dist/chunks/chunk-QXRJ52T4.js:2810:11)
at [nodejs.internal.kHybridDispatch] (node:internal/event_target:843:20)
at AbortSignal.dispatchEvent (node:internal/event_target:776:26)
at runAbort (node:internal/abort_controller:488:10)
at abortSignal (node:internal/abort_controller:459:3)
at AbortController.abort (node:internal/abort_controller:507:5)
at stopSpinner (file:///xx/pnpm/global/5/.pnpm/vercel@50.32.5_typescript@5.9.3/node_modules/vercel/dist/chunks/chunk-4S3Y3ATR.js:4321:22)
at processDeployment (file:///xx/pnpm/global/5/.pnpm/vercel@50.32.5_typescript@5.9.3/node_modules/vercel/dist/chunks/chunk-4S3Y3ATR.js:4474:9)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
> [debug] [2026-03-16T06:25:06.115Z] Error: ErrorCLI 的报错是“假象”,真正的错误在 Vercel 控制台。
进入:
👉 https://vercel.com → Deployments
出现状态为 Blocked 的记录,进入详情页面提示:
### Deployment Blocked
Git author xxx@users.noreply.github.com must have access to the team xxx's projects on Vercel to create deployments.
---
Hobby teams do not support collaboration. Please upgrade to Pro to add team members.
Learn More Upgrade to Pro
原因
不是 CLI 问题,也不是网络问题,而是部署身份校验失败。
曾经可能操作过:
- 注册两个或以上的
Vercel Hobby计划账号; - 两个
Vercel的项目使用一个GitHub账号维护; Vercel Hobby计划对绑定GitHub有限制,只能绑定一个;- 于是:
- 第一个使用
GitHub集成部署; - 第二个使用
Vercel CLI命令行部署,规避账号绑定数量的限制; - 之前是可以使用的,但是最近的规则调整了;
- 第一个使用
- 使用一个
GitHub账号提交,默认使用-global的配置; commit log中的author信息只对应第一个Vercel Hobby计划;- 第二个在提交的时候,触发了限制。
问题本质:身份体系冲突
本地机器
│
├─ Git
│ ├─ user.name
│ └─ user.email <- 决定 commit author
│
├─ SSH
│ ├─ private key
│ ├─ public key <- 决定 Github 身份
│ └─ ~/.ssh/config
│
├─ GitHub
│ ├─ 账号A
│ └─ 账号B
│
└─ Vercel
├─ 账号A / Team A
└─ 账号B / Team B
└─ 部署权限校验
本质:Git、SSH、GitHub、Vercel 这四套身份体系交叉了。
Git / SSH / Vercel 各自负责什么
SSH key解决的是:你是谁,能不能连接 GitHubgit user.name / user.email解决的是:这次 commit 是谁提交的
git log -1
# 输出
commit d2d24af5187906626f81ae382305848b7b7e8e63
Author: your-name <xxx@users.noreply.github.com>
Date: Mon Mar 16 17:43:04 2026 +0800GitHub 账号解决的是:仓库归谁、SSH key 属于谁- Vercel,决定谁可以部署,commit author 是否属于当前 team
Vercel 校验逻辑
在 Hobby 计划下,Vercel 可能会尝试确认:
- 当前部署的项目属于哪个账号 / team;
- 当前 Git commit 的作者是谁;
- 这个 commit author 是否有权限在该 team 下部署。
如果发现:
- commit author 不属于这个 team;
- 或者看起来像“另一个用户提交的代码”;
则会认定为“协作场景”。
但 Hobby 计划不支持这类私有仓库协作,于是部署会被直接 Blocked。
所以这个问题不是:
- SSH 连不上;
- Vercel CLI 坏了;
- GitHub push 失败;
而是:
部署身份校验失败。
💡 简单说:Vercel CLI 会读取当前 Git 仓库信息,会检查:
git log -1
# 输出
commit d2d24af5187906626f81ae382305848b7b7e8e63 (HEAD -> main, origin/main)
Author: [vercel_account] <xxx@users.noreply.github.com>
Date: Mon Mar 16 17:43:04 2026 +0800然后把这个 author 作为部署身份校验的一部分。
如果:
- 这个邮箱不是 Vercel account 的邮箱
- 或者这个邮箱属于另一个 GitHub 用户
- 或者这个邮箱不是 team owner
会直接拒绝部署:
- 认为是团队协作,相当于多个用户;
- Hobby 计划不支持团队协作;
- 如果需要多个用户部署,需要升级到 Pro。
Vercel 文档 Team configuration 说明了详细的限制。
Hobby teams The Hobby Plan does not support collaboration for private repositories. If you need collaboration, upgrade to the Pro Plan.
[Hobby 方案] 不支持私有仓库的团队协作。如果您需要协作功能,请升级至 [Pro 方案]。
To deploy commits under a Hobby team, the commit author must be the owner of the Hobby team containing the Vercel project connected to the Git repository. This is verified by comparing the Login Connections Hobby team's owner with the commit author.
若要在 Hobby 团队下部署提交(commit),提交者(commit author)必须是包含该 Vercel 项目的 Hobby 团队的所有者。系统会通过对比 Hobby 团队所有者的 [登录连接 (Login Connections)] 与提交者信息来验证身份。
To make sure we can verify your commits:
为了确保我们能成功验证您的提交,请执行以下操作:
- Make sure all commits are authored by the git user associated with your account. 确保所有提交的作者信息(git user)都与您的账号相关联。
- Link your git provider to your Vercel account in Account Settings 在 [账号设置 (Account Settings)] 中将您的 Git 服务商(如 GitHub/GitLab)连接至 Vercel 账号。
If your account is not connected to your git provider, make sure you've properly configured your Vercel email address so that it matches the email associated with the commit.
如果您的账号尚未连接 Git 服务商,请务必正确配置您的 [Vercel 邮箱地址],使其与提交记录中的邮箱保持一致。
For the most reliable experience, ensure both your project and account are properly connected to your git provider.
为了获得最稳定的体验,请确保您的项目和账号都已与 Git 服务商正确连接。
临时解决方案(修改 Git author)
- 进入仓库,确认 commit author
git log -1- 先看当前 Git 配置:
git config user.email- 改为正确账号
git config user.name "你的名字"
git config user.email "你的vercel邮箱" 最好使用仓库级:
git config --local user.name "你的名字"
git config --local user.email "你的邮箱"- 修正最近一次提交
git commit --amend --reset-author- 重新 push 和部署
git push --force-with-lease
vercel --prod根本解决方案(多账号隔离)
真实环境:
Mac
├─ GitHub 账号 A
│ └─ SSH key A
│
├─ GitHub 账号 B
│ └─ SSH key B
│
├─ SSH config
│ ├─ github.com → 账号A
│ └─ github-b → 账号B
│
└─ Vercel
├─ Account A
└─ Account B
- 同一台开发机器;
- 两个或多个 GitHub 账号;
- 两个或多个 SSH key;
- 两个或多个 Vercel 账号;
- 使用 SSH 连接 GitHub;
- 使用 Vercel CLI 进行部署;
隔离原则
目标:建立一套账号隔离规则,以项目为边界作为隔离,保持账号之间的纯洁度,确保不同项目之间不会混淆身份,避免类似的错误发生;
- 每个项目固定归属一个 GitHub 账号;
- 每个项目固定归属一个 Vercel 账号 / scope;
- 每个项目固定使用该账号对应的:
git user.namegit user.emailSSH key;
- 尽量不要让同一个项目中途换归属;真要换,就一次性迁移干净;
- 主账号仓库,只用主账号自己的 email / 主账号自己的 noreply
- 副账号仓库,只用副账号自己的 email / 副账号自己的 noreply
- 不要混用两个账号的 noreply
- SSH alias 必须和 remote 完全一致
配置
一台机器有多个 GitHub / Vercel 账号,不建议频繁修改--global
使用:
- 全局保留主账号默认值;
- 特殊项目在仓库内部使用
--local单独配置
步骤
为不同的 GitHub 账号分别生成私钥和公钥;
- 打开终端,生成新的 SSH key
ssh-keygen -t ed25519 -C "your_email@example.com"- 确认选项:
- 输入名称时,不要覆盖原有的私钥和公钥匙:
- 可以保留 passphrase 密码短语 为空;
> Generating public/private ALGORITHM key pair.
> Enter a file in which to save the key (/Users/YOU/.ssh/id_ALGORITHM): [Press enter]
> Enter passphrase (empty for no passphrase): [Type a passphrase]
> Enter same passphrase again: [Type passphrase again]将新生成的 私钥和公钥,重命名,自己可以区分就行:
id_ed25519_bid_ed25519_b.pub
- 将公钥上传至
GitHub
配置 ~/.ssh/config 为主账号和备用账号配置路由规则;
打开 ~/.ssh/config 文件,修改配置如下:
Host github.com
# github 主账号
AddKeysToAgent yes
UseKeychain yes
IdentityFile ~/.ssh/id_ed25519_main
IdentitiesOnly yes
host github-b
# github 备账号
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_b
IdentitiesOnly yes
调整仓库信息
此处记录的是将原有的项目迁移到新的GitHub账号
- 新账号下创建私有仓库;
- 将原有项目拷贝到新仓库预备的目录;
初始化仓库
git init给这个仓库单独配置 B 账号的身份
git config user.name "B账号的GitHub名称"
git config user.email "B账号的noreply邮箱"确认配置是否生效
git config --local --get user.name
git config --local --get user.email- 添加远程仓库
现在 SSH 的别名是github-b,不要使用默认的 github.com
git remote add origin git@github-b:[account]/[repo_name].git检查
git remote -v提交首个版本
git add .
git commit -m "Initial commit"把默认分支设为 main
git branch -M main首次推送
git push -u origin main验证发布
vercel --prodSSH 基础知识
当执行:
git clone git@github.com:someone/repo.git
# 或者
git pushGit 借助 SSH 去连接 GitHub,本机会做以下几件事:
- 要连接的是哪个主机,比如
github.com - 查
~/.ssh/config,看看这个主机该用什么规则 - 决定拿哪把私钥去尝试认证
- GitHub 收到公钥指纹后,判断是不是它认识的那个账号
- 如果对应得上,就允许 clone / push
所以:
- 私钥在本地
- 公钥上传到 GitHub
- config 决定“访问某个主机时优先用哪把钥匙”
SSH相关文件基本是在目录:~/.ssh,可能包含的文件列表如下:
| 文件 | 说明 |
|---|---|
| config | SSH 客户端的路由和规则表 |
| id_ed25519 | 私钥文件,非常重要,不能泄漏 |
| id_ed25519.pub | 公钥文件,用于上传至 GitHub |
| known_hosts | 以前连过哪些 SSH 服务器,以及它们的主机指纹是什么 |
| authorized_keys | 服务端使用 |
~/.ssh/config
最需要理解的:
- 是:SSH 客户端“按主机匹配规则的配置文件”;
- 核心作用不是“存密钥”;
- 是告诉 SSH:连接某个目标时,该用哪套连接参数;
- 类似“别名路由表”;
决定了:
- 连哪个主机
- 用哪个用户名
- 用哪把私钥
- 是否加到 agent
- 是否使用 macOS keychain
- 是否启用某些连接选项
核心参数:
| 参数 | 说明 | |
|---|---|---|
Host | 定义一个规则块。 | 比如:Host github.com当 ssh 连接 github.com使用该规则块的配置 |
HostName | 主机的别名。 | 比如:Host github-a HostName github.comgithub-a 实际连接 github.com |
User | SSH 登录用户名,不同的服务器不同 | |
| 服务 | SSH User | |
| GitHub | git | |
| GitLab | git | |
| AWS EC2 | ec2-user | |
| Ubuntu | ubuntu | |
| 所以,User git 意思是:ssh git@github.com | ||
IdentityOnly | yes 只使用 config 指定的 key不尝试其他的 key | |
IdentityFile | 对应的私钥 | |
AddKeysToAgent | 当 SSH 使用这把 key 时,自动加入 ssh-agent | 参见 ssh-agent |
UseKeychain | macOS 把 SSH 密钥的 passphrase 存进 Keychain |
比如:
Host *
ServerAliveInterval 60
Host github.com
AddKeysToAgent yes
UseKeychain yes
IdentityFile ~/.ssh/id_ed25519_a
Host github-b
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_b
IdentitiesOnly yes
- Host github.com 和 Host github-b 本地自定义的别名
- 最终都指向 github.com;
- 但会分别使用不同的私钥;
于是:
git clone git@github.com:账号A/repo.git
会走账户 A 的key
git clone git@github-b:账号B/repo.git
会走账号 B 的 key。
这就是 config 最强大的地方,也是账户隔离的基础。
Host *
表示“所有 SSH 主机都套用这个规则,每 60 秒发一个保活包,减少连接空闲断开。
Host github.com
表示当你连接 github.com 时,用下面这些规则:
-
AddKeysToAgent yes
这把 key 可以自动加入 ssh-agent,避免为此输入: -
UseKeychain yes
macOS 可把私钥口令记到钥匙串里 -
IdentityFile ~/.ssh/id_ed25519
连接 GitHub 时,优先使用这把私钥,对应的公钥需要上传到 GitHub,在进行 SSH 连接时的验证。
ssh-agent
如果没有 ssh-agent,SSH 的工作流程是这样的:
- 执行
git push- SSH 连接
github.com - SSH 需要使用私钥
- 如果私钥有密码(passphrase),系统会问你:
Enter passphrase for key ~/.ssh/id_ed25519
- 输入密码后,SSH 才能使用这把私钥。
ssh-agent 是一个 本地后台进程,作用是: 缓存已经解锁的私钥,流程变成:
- 第一次输入私钥密码
ssh-agent记住这个私钥- 以后 SSH 连接时直接向 agent 要签名
- 不再要求输入密码
所以:
ssh-agent = 本地私钥缓存代理
~/.ssh/id_ed25519 和 ~/.ssh/id_ed25519.pub
生成的私钥和公钥匙
~/.ssh/authorized_keys
作用:
- 当别人 SSH 登录到这台机器时
- 系统会检查
~/.ssh/authorized_keys - 里面列出来的公钥,才允许登录
也就是说,它更像是:
“哪些公钥被允许登录这台机器”
所以在本机上,这个文件往往用于:
- 远程登录到你的 Mac
- 某些内部系统 / 跳板机 / git 服务
- 某些容器或自动化工具写进去的允许登录公钥
~/.ssh/known_hosts
- 防止“中间人攻击”
- 核心安全设计之一
第一次连接服务器:
ssh root@xxSSH 会收到服务器的 host public key。
系统会提示:
The authenticity of host 'github.com' can't be established.
RSA key fingerprint is SHA256:xxxxx.
Are you sure you want to continue connecting (yes/no)?如果输入:
yesSSH 就会把这个指纹写入:
~/.ssh/known_hosts例如:
github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI...
第二次链接:
SSH 会做一个检查:
- 服务器给出的 host key
- 和
known_hosts里记录的 - 是否一致
如果一致:连接继续,否则:
WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!意味着:
- 服务器可能被替换
- DNS 被劫持
- 网络中间人攻击
SSH 会直接拒绝连接。
假设攻击者做了:
你 → 攻击者服务器 → GitHub
攻击者试图伪装成 GitHub。
但攻击者的 host key 不可能和 GitHub 一样。
所以:
SSH 会发现:
known_hosts 的 key ≠ 当前服务器 key
然后拒绝连接。
这就是 SSH 的 TOFU 模型:
Trust On First Use
首次连接信任
之后验证一致性Git 配置与 author 机制
- Git 配置分层;
- 如何查看当前生效的是哪一层;
git author和 SSH 身份不是一回事;
Git config
Git 配置主要决定两类事情:
- Git 的行为习惯
例如默认分支名、编辑器、换行符等; - 提交身份
例如:user.nameuser.email
例如:
git config user.name
git config user.email它们决定了 Git commit 里的作者信息。
查看最近一次提交:
git log -1
# 输出类似:
commit d2d24af5187906626f81ae382305848b7b7e8e63
Author: your-name <xxx@users.noreply.github.com>
Date: Mon Mar 16 17:43:04 2026 +0800这里的 Author,是 Vercel 会拿来校验的关键信息之一。
Git 配置通常有三层:
| 层级 | 命令 | 作用范围 |
|---|---|---|
| system | git config --system | 整台系统 |
| global | git config --global | 当前用户 |
| local | git config --local | 当前仓库 |
通常最常用的是:
--global--local
其中:
--global:当前用户所有仓库默认使用;--local:只对当前项目生效,优先级高于 global。
查看某个值:
git config user.name
git config user.email查看全局配置:
git config --global --list查看当前仓库配置:
git config --local --list查看所有配置以及来源:
git config --list --show-origin这个命令非常有用,可以看到某个配置到底来自:
~/.gitconfig- 当前仓库
.git/config - 还是系统配置文件
命令速查表
1. Git:查看当前作者信息
git config user.name
git config user.email作用:查看当前仓库实际生效的 Git 作者信息。
2. Git:查看全局配置
git config --global --list作用:查看当前用户级别的 Git 配置。
3. Git:查看当前仓库配置
git config --local --list作用:查看当前仓库自己的配置。
4. Git:查看配置来源
git config --list --show-origin作用:查看某个配置到底来自哪里。
5. Git:查看最近一次提交作者
git log -1作用:
- 查看最近一次 commit 的作者信息;
- Vercel 部署校验时会参考这里的 author。
6. Git:设置当前仓库作者信息
git config --local user.name "你的名字"
git config --local user.email "你的邮箱"作用:
- 只修改当前仓库的作者身份,不影响其他项目。
7. Git:修正最近一次提交作者
git commit --amend --reset-author作用:用当前 Git 配置重写最近一次 commit 的作者信息。
8. Git:查看远程仓库地址
git remote -v作用:
- 查看 fetch / push 使用的远程地址;
- 排查 SSH alias 是否写对。
9. Git:修改远程仓库地址
git remote set-url origin git@github-b:account/repo.git作用: 修改当前仓库 remote。
10. SSH:查看本地密钥文件
ls -la ~/.ssh作用:查看 SSH 相关文件是否存在。
11. SSH:查看 agent 中已加载的 key
ssh-add -l作用:查看 ssh-agent 当前缓存了哪些私钥。
12. SSH:测试 GitHub SSH 连接
ssh -T git@github.com
ssh -T git@github-b作用:测试当前 Host alias 是否能正确认证到对应 GitHub 账号。
13. SSH:查看详细连接过程
ssh -vT git@github-b作用:查看 SSH 实际命中了哪条配置、尝试了哪把 key。
14. Vercel:查看当前登录账号
vercel whoami作用:确认当前 CLI 登录的是哪个 Vercel 账号。
15. Vercel:查看调试日志
vercel --prod --debug作用:
- 查看 CLI 输出的调试信息;
- 但最终还是要结合控制台
Deployments页面判断真实错误。