Skip to content

node: match Node's validateArray minLength error message - #38410

Open
robobun wants to merge 1 commit into
mainfrom
farm/76b789ce/validate-array-min-length-message
Open

robobun wants to merge 1 commit into
mainfrom
farm/76b789ce/validate-array-min-length-message

Conversation

@robobun

@robobun robobun commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

Problem

  • validateArray(value, name, minLength) (the native internal/validators export) throws a malformed ERR_INVALID_ARG_VALUE when the array is shorter than minLength:
    • bun: The argument 'type string ('foo')' must be longer than 1. Received []
    • node v26.3.0: The argument 'foo' must have a length of at least 1. Received []
  • Name rendering: src/jsc/bindings/NodeValidator.cpp:354 passes the name as a JSValue, and the JSValue-name overload of Bun::ERR::INVALID_ARG_VALUE (src/jsc/bindings/ErrorCode.cpp:1048) ran it through determineSpecificType(), the helper that describes a received value (type string ('foo')). It also always wrote The argument, where node writes The property for dotted names such as options.foo. That overload had no other callers.
  • Reason text: both validateArray variants (NodeValidator.cpp:354 and :375) say must be longer than N; node's lib/internal/validators.js says must have a length of at least N.
  • Only reachable today through bun:internal-for-testing / vendored node tests: every in-tree validateArray caller passes minLength 0 or undefined.

Fix

  • The JSValue-name INVALID_ARG_VALUE overload now converts the name with toWTFString() and delegates to the WTF::String-name overload, which already produces node's The argument '<name>' / The property '<dotted.name>' form and the same Received rendering. Fixing the overload rather than the call site means any future JSValue-name caller gets the right message too.
  • Both validateArray variants use node's reason text.
  • Name conversion uses ToString semantics, the same thing node's template literal does, so a Symbol or throwing toString() name propagates the same error node raises.
  • The Rust-side validate_array in src/runtime/node/util/validators.rs is a separate helper with its own message shape and is not touched here; its min_length is never set in-tree.
  • Verified:
    • test/js/node/internal/validators.test.ts (new): the two minLength cases fail on the released binary and pass with this change; the non-array and success cases pass both ways and guard the unchanged paths.
    • test-internal-validators-validateoneof.js, test-internal-validators-validateport.js, test-child-process-constructor.js, test-dns-setservers-type-check.js, test-process-setgroups.js, test-vm-basic.js, test-worker-process-argv.js and the argv/execArgv tests in worker_threads.test.ts pass (the callers of both validateArray variants).
    • The new test also passes under BUN_JSC_validateExceptionChecks=1, including names whose toString() throws.
  • The ASCIILiteral-name variant's new text is only reachable from C++ callers (JSWorker.cpp, BunProcess.cpp), all of which pass minLength 0, so it has no JS-observable test; it is changed for consistency with the JSValue variant.

Background

  • internal/validators is node's module of argument checkers (validateArray, validateOneOf, ...). Bun implements them natively in src/jsc/bindings/NodeValidator.cpp; tests reach them through exposedInternals in bun:internal-for-testing, the same hook that serves vendored node tests declaring --expose-internals.
  • Bun::ERR::* in src/jsc/bindings/ErrorCode.cpp builds node-style coded errors. INVALID_ARG_VALUE is overloaded on the name type (ASCIILiteral, WTF::String, JSValue); the native validators use the JSValue overloads because they receive the name straight from JS.
  • determineSpecificType() is the port of node's helper of the same name: it describes a received value for ERR_INVALID_ARG_TYPE messages (type string ('foo'), an instance of Array). It is the wrong tool for rendering an argument name.
  • Node's ERR_INVALID_ARG_VALUE message is The <argument|property> '<name>' <reason>. Received <inspect(value)>, where property is used when the name contains a ..

The JSValue-name INVALID_ARG_VALUE overload rendered the name through
determineSpecificType, producing "The argument 'type string ('foo')'",
and never switched to "The property" for dotted names. It now
stringifies the name and delegates to the String-name overload. Both
validateArray variants use node's reason text, "must have a length of
at least N", instead of "must be longer than N".
@coderabbitai

coderabbitai Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@robobun, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 43 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 067e5f9b-20b9-4a7a-a6f0-473aabba8be0

📥 Commits

Reviewing files that changed from the base of the PR and between 01aa7cd and a56806d.

📒 Files selected for processing (3)
  • src/jsc/bindings/ErrorCode.cpp
  • src/jsc/bindings/NodeValidator.cpp
  • test/js/node/internal/validators.test.ts

Comment @coderabbitai help to get the list of available commands.

@robobun

robobun commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator Author

Status

  • Reproduced on the released binary (1.4.0) and on main (a0921e1) with exposedInternals["internal/validators"].validateArray([], "foo", 1): The argument 'type string ('foo')' must be longer than 1. Received []; node v26.3.0 prints The argument 'foo' must have a length of at least 1. Received [].
  • Fix: the JSValue-name INVALID_ARG_VALUE overload in ErrorCode.cpp now stringifies the name and delegates to the WTF::String overload; both validateArray variants in NodeValidator.cpp use node's reason text.
  • Test: test/js/node/internal/validators.test.ts (fails on the released binary, passes with this branch).
  • No overlap with node: render native validateOneOf errors like Node and type-check vm microtaskMode #38401 (span validateOneOf overloads) or node: match Node.js validation error wording for dotted option/type names #34620 (JSArray validateOneOf overload); different functions in the same files.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — small Node-compat fix for validateArray's minLength error message.

What was reviewed:

  • INVALID_ARG_VALUE JSValue-name overload now delegates to the WTF::String overload after toWTFString() + RELEASE_RETURN_IF_EXCEPTION; confirmed the target overload already handles the argument/property split and Received rendering.
  • Both validateArray variants get Node's reason text; the changed overload has no other callers so no message regressions elsewhere.
  • New test asserts exact error name/code/message for both name shapes plus the unchanged non-array path; PR verified under BUN_JSC_validateExceptionChecks=1.
Extended reasoning...

Overview

This PR fixes a malformed ERR_INVALID_ARG_VALUE message from the native validateArray when an array is shorter than minLength. Three files: ErrorCode.cpp rewrites the JSValue-name INVALID_ARG_VALUE overload to convert the name via toWTFString() and delegate to the existing WTF::String overload (which already produces Node's The argument '<name>' / The property '<dotted.name>' shape); NodeValidator.cpp updates both validateArray variants' reason text from "must be longer than N" to Node's "must have a length of at least N"; and a new test file asserts the exact messages against Node v26.3.0's output.

Security risks

None. This is error-message text formatting in a Node-compat validator. No parsing of untrusted input, no new allocations driven by user-controlled sizes, no auth/crypto/filesystem paths.

Level of scrutiny

Low. The path is only reachable via bun:internal-for-testing today (all in-tree validateArray callers pass minLength 0 or undefined per the PR description). The C++ change replaces a hand-rolled StringBuilder with delegation to an existing, already-correct overload — strictly a simplification. toWTFString() is guarded by RELEASE_RETURN_IF_EXCEPTION, and the delegated overload operates on the same ThrowScope& and does its own throwException + release(), so exception-scope discipline is preserved (verified under BUN_JSC_validateExceptionChecks=1 per the PR).

Other factors

The fix is at the right layer (the overload, not the call site), so future JSValue-name callers get the correct rendering. The old overload's use of determineSpecificType() on the name was clearly a bug — that helper describes a received value. The new test asserts name, code, and full message for the argument-name case, the dotted property-name case, and the unchanged non-array ERR_INVALID_ARG_TYPE path, plus a positive case guarding no-throw. The ASCIILiteral-name variant's text change is untested from JS but is a two-word string edit for consistency and has no live callers with nonzero minLength.

@robobun

robobun commented Aug 14, 2026 •

Copy link
Copy Markdown
Collaborator Author
Updated 5:05 AM PT - Aug 14th, 2026

❌ @robobun, your commit a56806d has some failures in Build #95651 (All Failures)


🧪   To try this PR locally:

bunx bun-pr 38410

That installs a local version of the PR into your bun-38410 executable, so you can run:

bun-38410 --bun

@robobun

robobun commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator Author

This bug was reported again during triage on 2026-08-21. Main (4448a2e) still prints The argument 'type string ('foo')' must be longer than 3. Received [ 1, 2 ] for validateArray([1, 2], 'foo', 3). This PR is the fix for it, so no second PR was opened.

One data point for a follow-up. The triage run executed node v26.3.0's test/parallel/test-validators.js against main. Its validateArray block (lines 97 to 103, asserts at least 3) needs this change. Its validateObject block needs the kValidateObjectAllow* flags from #39920. Once both land, that file can be vendored into test/js/node/test/parallel/ as is.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant