fix(vercel-api): normalize engines.node and drop nowConfig payload field - #364
Conversation
The Vercel REST API only accepts "24.x", "22.x", or "20.x" for
projectSettings.nodeVersion. v42.2.1 forwarded the raw `engines.node`
value verbatim, so consumers with semver ranges (">=24.0.0", "^20.0.0")
or exact versions ("24.0.0") hit 400 bad_request.
Add a local `normalizeNodeVersion` helper using `semver.intersects`
against a hardcoded list of supported Vercel majors. Map any matching
range to the canonical "NN.x" form. For unsupported inputs, omit the
field with a warning so the deployment falls back to the project default
instead of failing.
`semver` is promoted from a transitive dependency to a direct one, with
`@types/semver` for type support.
Refs #359
The Vercel REST API rejects nowConfig as an undeclared additional property (HTTP 400). PR #350 attached it to DeploymentOptions based on a misreading of the API contract; the Vercel CLI never sends it. Mirror the CLI by copying a static whitelist of vercel.json keys — buildCommand, installCommand, outputDirectory, framework, devCommand — into projectSettings instead. Drop the nowConfig field from ProjectConfig and the assignment in applyProjectConfig. Update tests that asserted the broken behavior. The static whitelist also makes the prior images / __proto__ / constructor / prototype stripping unnecessary by construction. Refs #359
Combine both bugs into one scenario at unit and integration levels: package.json with engines.node ">=24.0.0" plus vercel.json with buildCommand. Assert the resulting payload normalizes nodeVersion to "24.x", forwards buildCommand via projectSettings, and contains no top-level nowConfig. The integration test additionally tolerates known emulator network errors but rejects any error message mentioning projectSettings.nodeVersion or nowConfig — these would indicate the bugs have re-emerged. Refs #359
Bundles semver dependency, normalizeNodeVersion helper, and the projectSettings whitelist replacing nowConfig. Refs #359
Reviewer caught divergence from `@vercel/build-utils.getSupportedNodeVersion`, which iterates supported majors in descending order and returns the first intersecting match. Flipping VERCEL_NODE_VERSIONS to ['24.x', '22.x', '20.x'] means `engines.node: '>=18'` now resolves to '24.x' (matching `vc deploy`) instead of '20.x'. Also clarify the unsupported-version warning to distinguish discontinued majors (e.g. 18.x) from malformed input, and add an explicit test for the discontinued-major case. Refs #359
|
Deploy preview for file ready!
Deployed with vercel-action |
|
Deploy preview for static ready!
Deployed with vercel-action |
|
Deploy preview for express-basic-auth ready!
Deployed with vercel-action |
There was a problem hiding this comment.
Pull request overview
Fixes Vercel REST API validation regressions introduced in v42.2.1 by (1) normalizing projectSettings.nodeVersion to Vercel’s accepted "NN.x" enum and (2) removing the invalid top-level nowConfig payload field, while still honoring key vercel.json settings via projectSettings.
Changes:
- Add
normalizeNodeVersion()(semver-based) and omitprojectSettings.nodeVersionwhen it can’t be mapped to24.x | 22.x | 20.x(warn instead of hard-failing). - Drop
nowConfigentirely; copy a small whitelist ofvercel.jsonkeys intoprojectSettings. - Update unit + integration tests and rebuild
dist/artifacts.
Reviewed changes
Copilot reviewed 12 out of 17 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/project-config.ts | Adds node version normalization and copies whitelisted vercel.json keys into projectSettings (removes nowConfig). |
| src/vercel-api.ts | Stops attaching nowConfig to DeploymentOptions; updates comment to reflect REST API constraints. |
| src/tests/project-config.test.ts | Adds unit coverage for normalization + locks down the projectSettings whitelist behavior. |
| src/tests/vercel-api.test.ts | Updates deployment payload assertions to ensure no nowConfig is sent and projectSettings is populated. |
| src/integration/vercel-api.test.ts | Adds emulator-based regression coverage for the combined engines.node + vercel.json scenario. |
| package.json | Promotes semver to a direct dependency; adds @types/semver. |
| pnpm-lock.yaml | Records the new direct dependency/type additions and lockfile updates. |
| dist/index.js | Rebuilt bundle including semver and the updated deployment payload behavior. |
| dist/project-config.d.ts | Updates typings: removes nowConfig, adds new projectSettings keys and normalizeNodeVersion() export. |
| dist/licenses.txt | Adds the semver license text to the bundled licenses. |
| .please/docs/tracks/active/fix-vercel-validation-20260430/spec.md | Adds written spec for the regression and intended fixes. |
| .please/docs/tracks/active/fix-vercel-validation-20260430/plan.md | Adds implementation plan and verification checklist. |
| .please/docs/tracks/active/fix-vercel-validation-20260430/investigation.md | Adds investigation notes and root-cause analysis for #359. |
| .please/docs/tracks/active/fix-vercel-validation-20260430/metadata.json | Adds track metadata for the bugfix workstream. |
| .please/docs/tracks.jsonl | Registers the new active track entry. |
| .please/docs/knowledge/tech-stack.md | Documents semver usage as part of the repo’s tech stack knowledge. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
0 issues found across 5 files (changes from recent commits).
Requires human review: Fixes a regression by modifying core logic in the deployment payload construction and adds 'semver' as a production dependency. Requires human review to verify the normalization logic.
There was a problem hiding this comment.
Code Review
This pull request resolves deployment validation errors (Issue #359) by normalizing engines.node versions to Vercel's canonical NN.x format and replacing the nowConfig field with a whitelisted set of projectSettings. The implementation introduces the semver package for version range intersection and includes updated unit and integration tests. Feedback suggests adding runtime type validation when copying values from vercel.json to projectSettings to prevent potential API errors from non-string inputs.
There was a problem hiding this comment.
1 issue found across 17 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/project-config.ts">
<violation number="1" location="src/project-config.ts:133">
P2: Use an own-property check instead of `in` so only keys explicitly present in `vercel.json` are copied.
(Based on your team's feedback about asserting own properties when guarding against prototype-pollution behavior.) [FEEDBACK_USED]</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
Apply suggestions from cubic, gemini, and copilot review threads on PR #364: - Use `Object.hasOwn(vercelJson, key)` instead of `key in vercelJson` so prototype-chain properties cannot leak into projectSettings even if upstream code mutates Object.prototype (cubic, copilot). - Type-guard whitelisted vercel.json values: only string and null pass through to projectSettings. Other types surface a local warning instead of producing a 400 from the Vercel API (gemini). Add tests for both behaviors: explicit null sentinel preservation and rejection of non-string/non-null values. Refs #359, PR #364
|
There was a problem hiding this comment.
0 issues found across 4 files (changes from recent commits).
Requires human review: This PR modifies the request shape sent to the Vercel API and introduces logic for Node version normalization, which falls under core logic and API changes requiring human review.


Summary
Fixes two deployment-failure regressions in v42.2.1 (PR #350) reported in #359.
Bug 1 —
projectSettings.nodeVersionrejected (HTTP 400)The Vercel REST API only accepts the canonical shorthand
"24.x","22.x", or"20.x". v42.2.1 forwardedpackage.jsonengines.nodeverbatim, so consumers with semver ranges (>=24.0.0,^20.0.0) or exact versions (24.0.0) hitInvalid request: projectSettings.nodeVersion should be equal to one of the allowed values "24.x, 22.x, 20.x".Fix: New
normalizeNodeVersionhelper insrc/project-config.tsusingsemver.intersectsagainst a hardcoded list of supported Vercel majors. Iteration is highest-first to mirror Vercel CLI parity (@vercel/build-utils.getSupportedNodeVersionwalks[24, 22, 20]descending). For unsupported inputs the field is omitted with acore.warning, so the deployment falls back to the project's default Node version instead of failing.Bug 2 —
nowConfigrejected as additional property (HTTP 400)v42.2.1 attached
nowConfig(parsedvercel.jsoncontents) as a top-level field onDeploymentOptions.@vercel/client.postDeploymentraw-spreadsDeploymentOptionsinto the request body, so the Vercel REST API rejectednowConfigasadditionalProperty. The Vercel CLI does not sendnowConfig; it copies a select set ofvercel.jsonkeys intoprojectSettingsinstead.Fix: Drop the
nowConfigfield entirely. ReplacesanitizeNowConfigwith a staticPROJECT_SETTINGS_KEYSwhitelist (buildCommand,installCommand,outputDirectory,framework,devCommand) copied intoprojectSettingsfromvercel.json. The static whitelist also makes the prior prototype-pollution stripping unnecessary by construction.Changes
package.json— promotesemverfrom transitive to direct dep; add@types/semver.src/project-config.ts— addnormalizeNodeVersion; replacenowConfigwithPROJECT_SETTINGS_KEYSwhitelist; warning path for unsupportedengines.node.src/vercel-api.ts— dropnowConfigassignment inapplyProjectConfig; correct misleading comment block.src/__tests__/project-config.test.ts— 11 new normalization cases + lockdown for projectSettings whitelist.src/__tests__/vercel-api.test.ts— rewrite the three tests that asserted the brokennowConfigbehavior; add a combined regression test.src/__integration__/vercel-api.test.ts— emulator-based regression test for semver-rangeengines.node+vercel.json buildCommandtogether.dist/— rebuilt.Verification Checklist
pnpm run all(lint + typecheck + build + test) passes locally.GITHUB_REPOSITORY=test-owner/test-repo pnpm test— full suite green (249/249).pnpm test:coverage—project-config.ts93.63%,vercel-api.ts90.78% (no regression).pnpm run build—dist/index.jsrebuilt and committed.engines.node: ">=24.0.0"andvercel.json: { "buildCommand": "./build.sh" }and confirm deployment succeeds against a real Vercel project.Track
.please/docs/tracks/active/fix-vercel-validation-20260430/— spec, investigation, plan, and progress log.Closes #359
Summary by cubic
Normalize
engines.nodeto Vercel’sNN.xenum, drop the rejectednowConfig, and hardenvercel.jsonkey handling to prevent API 400s. Restores deployments, matches Vercel CLI behavior, and fixes #359.Bug Fixes
package.jsonengines.nodeto "24.x", "22.x", or "20.x" usingsemver(highest-first to match CLI). Omit unsupported or discontinued inputs with a warning.nowConfig. Copy only whitelistedvercel.jsonkeys intoprojectSettings(buildCommand,installCommand,outputDirectory,framework,devCommand) whenObject.hasOwn(vercelJson, key)is true and the value is a string or null; warn on other types.Dependencies
semverand@types/semver.Written for commit bf518df. Summary will update on new commits. Review in cubic