Vercel Hobby: vercel --prod Unexpected error

Root cause of Unexpected error when deploying with vercel --prod on Vercel Hobby: not CLI or network—Git commit author vs. Vercel team permission check (multi GitHub/SSH/Vercel account). Full troubleshooting and solutions.

VercelVercel Hobbyvercel --prodVercel Unexpected errorVercel Deployment BlockedVercel commit authorVercel Git author verificationVercel multi-accountGitHub multi-account SSHgit user.emailgit commit authorSSH config multi-accountVercel CLI deployment failureVercel team permissionsHobby plan limits

Symptoms

On the Hobby plan, when deploying with the vercel CLI you see: 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. ()

With debug output (vercel --prod --debug), you see errors like:

[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: Error

The CLI error is misleading; the real error is in the Vercel dashboard.

Go to:

👉 https://vercel.comDeployments

You’ll see a deployment with status Blocked. Opening it shows:

### 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

Root cause

It’s not a CLI bug or a network issue—it’s deployment identity verification failing.

Typical setup:

  1. You have two or more Vercel Hobby accounts;
  2. Two Vercel projects are maintained with one GitHub account;
  3. Vercel Hobby only allows linking one GitHub account;
  4. So:
    1. The first project uses GitHub integration for deployment;
    2. The second uses the Vercel CLI to work around the one-account limit;
    3. This used to work, but recent policy changes broke it;
  5. Commits are made with one GitHub account, usually using --global Git config;
  6. The author in the commit log only matches the first Vercel Hobby account;
  7. Deploying the second project triggers the restriction.

The real issue: identity mismatch

Local machine  
│  
├─ Git  
│ ├─ user.name  
│ └─ user.email      <- determines commit author
│  
├─ SSH  
│ ├─ private key  
│ ├─ public key      <- determines GitHub identity
│ └─ ~/.ssh/config  
│  
├─ GitHub  
│ ├─ Account A  
│ └─ Account B  
│  
└─ Vercel  
├─ Account A / Team A  
└─ Account B / Team B
└─ deployment permission check

In short: Git, SSH, GitHub, and Vercel are four separate identity systems that can conflict.

What Git / SSH / Vercel each do

  • SSH key: who you are for connecting to GitHub
  • git user.name / user.email: who is recorded as the author of each commit
git log -1
 
# output
 
commit d2d24af5187906626f81ae382305848b7b7e8e63
Author: your-name <xxx@users.noreply.github.com>
Date: Mon Mar 16 17:43:04 2026 +0800
  • GitHub account: who owns the repo and which SSH key is used
  • Vercel: who can deploy, and whether the commit author belongs to the current team

Vercel verification logic

On the Hobby plan, Vercel checks:

  1. Which account/team the project belongs to;
  2. Who the current Git commit author is;
  3. Whether that commit author is allowed to deploy for that team.

If it finds:

  • the commit author is not in this team, or
  • it looks like “code committed by another user”,

it treats it as a collaboration scenario.

The Hobby plan does not support that for private repos, so the deployment is Blocked.

So the issue is not:

  • SSH failing;
  • Vercel CLI broken;
  • GitHub push failing;

but:

Deployment identity verification failed.

In short: the Vercel CLI reads the current Git repo and checks:

git log -1
 
# output
commit d2d24af5187906626f81ae382305848b7b7e8e63 (HEAD -> main, origin/main)
Author: [vercel_account] <xxx@users.noreply.github.com>
Date:   Mon Mar 16 17:43:04 2026 +0800

That author is used for deployment identity verification.

If:

  • this email is not the Vercel account email, or
  • it belongs to another GitHub user, or
  • it is not the team owner,

deployment is rejected:

  • Vercel treats it as team collaboration (multiple users);
  • Hobby does not support collaboration;
  • For multiple users to deploy, you need to upgrade to Pro.

Vercel docs: Team configuration describes these limits in detail.

Hobby teams The Hobby Plan does not support collaboration for private repositories. If you need collaboration, upgrade to the Pro Plan.

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.

To make sure we can verify your commits:

  1. Make sure all commits are authored by the git user associated with your account.
  1. Link your git provider to your Vercel account in Account Settings

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.

For the most reliable experience, ensure both your project and account are properly connected to your git provider.

Quick fix (change Git author)

  1. In the repo, confirm the commit author:
git log -1
  1. Check current Git config:
git config user.email
  1. Set to the correct account:
git config user.name "Your Name"
git config user.email "your-vercel-email@example.com" 

Prefer repo-level config:

git config --local user.name "Your Name"  
git config --local user.email "your-email@example.com"
  1. Fix the latest commit:
git commit --amend --reset-author
  1. Push and deploy again:
git push --force-with-lease
 
vercel --prod

Long-term fix (multi-account isolation)

Typical setup:

Mac  
├─ GitHub Account A  
│ └─ SSH key A  
│  
├─ GitHub Account B  
│ └─ SSH key B  
│  
├─ SSH config  
│ ├─ github.com → Account A  
│ └─ github-b → Account B  
│  
└─ Vercel  
├─ Account A  
└─ Account B
  • One dev machine;
  • Two or more GitHub accounts;
  • Two or more SSH keys;
  • Two or more Vercel accounts;
  • SSH for GitHub;
  • Vercel CLI for deployment.

Isolation principles

Goal: isolate by project so each project uses exactly one identity and you avoid the above errors.

  • Each project is tied to one GitHub account;
  • Each project is tied to one Vercel account/scope;
  • Each project consistently uses that account’s:
    • git user.name
    • git user.email
    • SSH key;
  • Avoid changing a project’s “owner” mid-way; if you do, migrate it fully once;
  • Main-account repos: only main account’s email / main account’s noreply;
  • Secondary-account repos: only that account’s email / that account’s noreply;
  • Don’t mix noreply addresses across accounts;
  • SSH alias must match the remote exactly.

Config

With multiple GitHub/Vercel accounts on one machine, avoid constantly changing --global.

Use:

  • Global config for your primary account;
  • --local in each repo for projects that use a different account.

Steps

Generate separate private/public keys for each GitHub account;

GitHub Docs

  1. Open a terminal and generate a new SSH key:
ssh-keygen -t ed25519 -C "your_email@example.com"
  1. When prompted:
    • Use a different path so you don’t overwrite existing keys;
    • You can leave the passphrase empty;
> 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]

Rename the new key pair so you can tell them apart, e.g.:

  • id_ed25519_b
  • id_ed25519_b.pub
  1. Add the public key to your GitHub account.

Configure ~/.ssh/config for main and secondary accounts;

Edit ~/.ssh/config like this:

Host github.com
    # primary GitHub account
    AddKeysToAgent yes
    UseKeychain yes
    IdentityFile ~/.ssh/id_ed25519_main
    IdentitiesOnly yes

host github-b
    # secondary GitHub account
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_ed25519_b
    IdentitiesOnly yes

Adjust repo setup

This describes migrating an existing project to a new GitHub account.

  1. Create a private repo under the new account;
  2. Copy the project into the new repo’s directory;

Initialize the repo:

git init

Set this repo to use Account B’s identity:

git config user.name "Account B's GitHub username"  
git config user.email "account-b-noreply@users.noreply.github.com"

Verify the config:

git config --local --get user.name  
git config --local --get user.email
  1. Add the remote

Use the SSH alias github-b, not the default github.com.

git remote add origin git@github-b:[account]/[repo_name].git

Check:

git remote -v

First commit:

git add .  
git commit -m "Initial commit"

Set default branch to main:

git branch -M main

First push:

git push -u origin main

Verify deployment:

vercel --prod

SSH basics

When you run:

git clone git@github.com:someone/repo.git
# or
git push

Git uses SSH to talk to GitHub. Your machine:

  1. Determines the host (e.g. github.com)
  2. Looks up ~/.ssh/config for that host
  3. Picks which private key to use
  4. GitHub checks the public key and matches it to an account
  5. If it matches, clone/push is allowed

So:

  • Private key stays on your machine
  • Public key is added to GitHub
  • config decides which key to use for which host

SSH-related files live under ~/.ssh. Typical contents:

FilePurpose
configSSH client host rules and routing
id_ed25519Private key; keep secret
id_ed25519.pubPublic key; add to GitHub
known_hostsHost keys of servers you’ve connected to
authorized_keysUsed on the server side

~/.ssh/config

What to remember:

  • It’s the SSH client’s “match host → use these options” file;
  • It doesn’t store keys; it tells SSH which key and options to use for each target;
  • Think of it as an alias/routing table.

It controls:

  • Which host to connect to
  • Which username
  • Which private key
  • Whether to add the key to the agent
  • Whether to use macOS Keychain
  • Other connection options

Main options:

OptionMeaning
HostStart a block for a host (e.g. Host github.com → use this block when connecting to github.com)
HostNameReal hostname when using an alias (e.g. Host github-a with HostName github.com → github-a connects to github.com)
UserSSH login user. Examples:
ServiceSSH User: GitHub → git, GitLab → git, AWS EC2 → ec2-user, Ubuntu → ubuntu. So User git means ssh git@github.com
IdentitiesOnlyyes = only use the key from config, don’t try other keys
IdentityFilePath to the private key
AddKeysToAgentWhen this key is used, add it to ssh-agent (see ssh-agent)
UseKeychainOn macOS, store the key’s passphrase in Keychain

For example:

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 and Host github-b are local aliases;
  • Both ultimately point to github.com but use different private keys.

So:

git clone git@github.com:accountA/repo.git

uses Account A’s key, and

git clone git@github-b:accountB/repo.git

uses Account B’s key. That’s the power of config and the basis for account isolation.

Host *

means “apply this to all SSH hosts”; here, send a keepalive every 60 seconds to avoid idle disconnects.

Host github.com

means “when connecting to github.com, use these options”:

  • AddKeysToAgent yes — add this key to ssh-agent so you don’t have to enter the passphrase repeatedly;
  • UseKeychain yes — on macOS, store the passphrase in Keychain;
  • IdentityFile ~/.ssh/id_ed25519 — use this private key for GitHub; the matching public key must be added to your GitHub account for SSH auth.

ssh-agent

Without ssh-agent, SSH works like this:

  1. You run git push
  2. SSH connects to github.com
  3. SSH needs the private key
  4. If the key has a passphrase, you’re prompted:
Enter passphrase for key ~/.ssh/id_ed25519
  1. After you enter it, SSH can use the key.

ssh-agent is a local background process that caches unlocked private keys:

  1. First time: you enter the passphrase
  2. ssh-agent remembers the key
  3. Later, SSH gets signatures from the agent
  4. No more passphrase prompts

So: ssh-agent = local private-key cache.

~/.ssh/id_ed25519 and ~/.ssh/id_ed25519.pub

The generated private and public key pair.

~/.ssh/authorized_keys

Used when someone SSHs into this machine:

  • The server checks ~/.ssh/authorized_keys
  • Only the public keys listed there are allowed to log in

So it answers: “which public keys are allowed to log in to this machine.” On your own Mac it’s used for:

  • Remote login to your Mac
  • Internal systems / jump hosts / Git servers
  • Keys added by containers or automation

~/.ssh/known_hosts

  • Protects against man-in-the-middle attacks
  • Core SSH security

On first connection to a host (e.g. ssh root@xx), SSH receives the server’s host public key and may prompt:

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)?

If you type yes, SSH stores the fingerprint in ~/.ssh/known_hosts, e.g.:

github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI...

On later connections, SSH checks that the server’s host key matches what’s in known_hosts. If it doesn’t:

WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!

That can mean the server was replaced, DNS was hijacked, or there’s a man-in-the-middle. SSH will refuse to connect.

If an attacker sits in the middle (you → attacker’s server → GitHub), they can’t present GitHub’s real host key, so the key won’t match known_hosts and SSH will refuse. This is SSH’s TOFU model:

Trust On First Use
Trust on first connection, verify consistency afterward

Git config and author

  1. Git config has layers;
  2. You can see which layer is in effect;
  3. git author and SSH identity are separate.

Git config

Git config mainly controls:

  1. Behavior — default branch, editor, line endings, etc.;
  2. Commit identityuser.name and user.email.
git config user.name  
git config user.email

These determine the author in each Git commit. To see the latest commit:

git log -1
 
# example output:
 
commit d2d24af5187906626f81ae382305848b7b7e8e63  
Author: your-name <xxx@users.noreply.github.com>  
Date:   Mon Mar 16 17:43:04 2026 +0800

That Author is what Vercel uses for verification.

Git config has three layers:

LayerCommandScope
systemgit config --systemWhole system
globalgit config --globalCurrent user
localgit config --localCurrent repo

You’ll mostly use --global and --local. Global applies to all repos for your user; local applies only to the current repo and overrides global.

View a value:

git config user.name
git config user.email

View global config:

git config --global --list

View repo config:

git config --local --list

View all config and where each value comes from:

git config --list --show-origin

Useful to see if a value comes from ~/.gitconfig, the repo’s .git/config, or system config.

Command reference

1. Git: current author

git config user.name  
git config user.email

Shows the author used for commits in this repo.

2. Git: global config

git config --global --list

Shows user-level Git config.

3. Git: repo config

git config --local --list

Shows this repo’s config.

4. Git: config origin

git config --list --show-origin

Shows where each config value comes from.

5. Git: latest commit author

git log -1

Shows the author of the latest commit; Vercel uses this for deployment checks.

6. Git: set repo author

git config --local user.name "Your Name"  
git config --local user.email "your-email@example.com"

Sets author only for this repo; other projects are unchanged.

7. Git: fix latest commit author

git commit --amend --reset-author

Rewrites the latest commit’s author using current Git config.

8. Git: remote URLs

git remote -v

Shows fetch/push URLs; use to verify SSH alias.

9. Git: change remote URL

git remote set-url origin git@github-b:account/repo.git

Updates the current repo’s remote.

10. SSH: list key files

ls -la ~/.ssh

Lists SSH-related files.

11. SSH: keys in agent

ssh-add -l

Lists private keys currently loaded in ssh-agent.

12. SSH: test GitHub connection

ssh -T git@github.com  
ssh -T git@github-b

Tests that each Host alias authenticates to the right GitHub account.

13. SSH: verbose connection

ssh -vT git@github-b

Shows which config and key SSH uses.

14. Vercel: current account

vercel whoami

Shows which Vercel account the CLI is using.

15. Vercel: debug logs

vercel --prod --debug

Shows CLI debug output; for the actual error, check the dashboard Deployments page.

暂无目录