I am seeing a reproducible deployment-target classification problem on a Vercel Hobby account. I am posting publicly to share the reproducible evidence and ask what CLI or REST request is supported for a guaranteed Preview deployment.
Expected behavior
Current Vercel documentation says:
- running
vercellocally creates a Preview build by default unless--prodis used: Builds vercel deploy --targetcan selectpreview: vercel deploy- the create-deployment API’s
targetdescription says an omitted target will bepreview: Create a new deployment | Vercel REST API
For a fresh project with no Git repository or branch metadata, I therefore expected an omitted target to produce:
target=preview
VERCEL_ENV=preview
VERCEL_TARGET_ENV=preview
Actual behavior
The only deployment created in each of three isolated fresh projects was classified as Production:
| Attempt | Git source/metadata | Request path | Result |
|---|---|---|---|
| Original dedicated project | No provider Git link; deployment API had gitSource=null |
Vercel CLI 52.0.0 with --target preview |
target=production |
| Disposable canary A | None; detached directory outside Git | Vercel CLI default deployment, target omitted | target=production |
| Disposable canary B | None | Direct POST /v13/deployments with inline files; target, customEnvironmentSlugOrId, gitSource, Git metadata, aliases and environment fields absent |
target=production |
The two disposable canaries used different fresh projects but byte-identical source files and equivalent project settings. Each canary project had:
- exactly one deployment;
- no Git link;
- no environment variables;
- no custom environments;
- no integrations;
- no custom domains;
- no requested alias or promotion;
- deployment protection retained.
Both canary deployment API read-backs returned:
target=production
readyState=ERROR
aliasAssigned=false
gitSource=null
meta={}
Each canary used a classification-only build script that emitted exactly one record containing three allowlisted values and then intentionally exited nonzero in every environment. Both records were:
{
"VERCEL_ENV": "production",
"VERCEL_TARGET_ENV": "production",
"OIDC_ENVIRONMENT": "production"
}
VERCEL_ENV and VERCEL_TARGET_ENV are documented system variables, and the OIDC environment claim is documented as development, preview, or production:
The intentional nonzero exits left both canaries in readyState=ERROR; neither reached a usable runtime, and no deployment URL was probed. The canaries contained no application source and used no custom domain, customer data, or provider integration.
Historical first-deployment behavior is a hypothesis, not a confirmed cause
Two 2019 issues in Vercel’s public repository document then-current first-deployment behavior:
- Render message after first deployment is created · Issue #2756 · vercel/vercel · GitHub — “Currently, we’re making the first deployment production…”
- Show message when creating first production deployment · Issue #2757 · vercel/vercel · GitHub — “Currently, we’re upgrading the first deployment to production…”
Issue #2756 was closed in favor of #2757. When #2757 was closed, its author commented, “This is implemented now.” Because the issue requested an explanatory message for the then-current behavior, that closure confirms the message work, not that a first-deployment upgrade remains active today. I have not found current documentation saying that a first deployment is upgraded to Production or that such an upgrade overrides the documented Preview/default request semantics.
All three observations occurred on a project’s first deployment, so the historical behavior is a plausible hypothesis. It is not a proven root cause, proof of current intentional behavior, or proof that the same implementation path handled these deployments. The two controlled no-Git canaries also weigh against Git metadata or a CLI-only payload path as the sole explanation, but they do not isolate an account, project-state, or backend cause.
CLI and API behavior observed
In the installed Vercel CLI 52.0.0 source, the deployment client normalizes a literal Preview target to an omitted request target before sending the deployment request:
if (deploymentOptions.target === "preview") {
deploymentOptions.target = void 0;
}
The detached CLI run reported that the source directory was not a Git repository and that it could not obtain a last commit. Its deployment-creation response still contained target:"production".
The direct REST canary removed the CLI from the request path and produced the same classification with no Git fields.
The current create-deployment API prose also leaves an ambiguity:
targetsays omission yields Preview;customEnvironmentSlugOrIdsays omission targets the environment inferred from the branch, either Production or Preview;targetlists omitted,staging,production, or a custom-environment identifier, but does not list a built-in literalpreviewrequest value.
Related public reports
These recent Community posts describe Preview requests or non-production branches being classified as Production. They are separate user reports, not proof of a shared cause:
- Preview branch and CLI deployments are always classified as Production
- Preview deployments incorrectly routed to Production
- Non-production Git branch classified as Production Deployment
Questions for Vercel
- Is the historical first-deployment upgrade to Production still active under any current conditions? If so, where is that exception documented?
- Does that behavior, when active, override CLI
--target previewor REST target omission? - Is literal REST
target:"preview"supported even though the current API prose does not list it as a built-in request value? - Is
customEnvironmentSlugOrIdrelevant to the built-in Preview environment, or only to separately created custom environments? - What exact supported CLI command or REST request guarantees built-in Preview for a fresh project’s first deployment?
- Can the CLI, Environments, and create-deployment API documentation be updated to describe any first-deployment exception that still exists?
I have preserved the inert failed deployments and sanitized request/read-back evidence. I can provide private resource identifiers to Vercel staff through a non-public channel if needed. I will not retry the real application until there is a provider-confirmed or independently verified request path that guarantees Preview.
This report shares evidence only. It does not authorize deployment, promotion, cleanup, domain changes, or testing against production or customer resources.