Repository navigation
Conversation
crypto.scrypt called its callback with (undefined, derivedKey) when the derivation worked. Node documents err as null in that case and passes null. A caller that tests err === null took a successful call for a failure.
|
Status: closed. #47559 landed this fix on main (bc55d56), with a test. How I reproduced it:
|
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 9 minutes. View limit details
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Updated 4:05 AM PT - Oct 17th, 2026
❌ @robobun, your commit 49d440e has 1 failures in
🧪 To try this PR locally: bunx bun-pr 45475That installs a local version of the PR into your bun-45475 --bun |
…pt-callback-null-error
|
Main covers this PR. PR #47559 (bc55d56) made the same one-line change in I checked this on a debug build of bc55d56:
This PR has nothing that main lacks. The one difference is that its test also makes the call without options, and both calls reach the same I did not rebase it, because the rebase leaves only that second test call. A maintainer can close it. |
Problem
crypto.scryptcalls its callback with(undefined, derivedKey)when the derivation works. Node passesnulland documents it: "erris an exception object when key derivation fails, otherwiseerrisnull".err === null, or runsassert.strictEqual(err, null), takes a call that worked for a failure.ScryptJob::then(src/runtime/node/node_crypto_binding.rs:1152). It starts withJSValue::UNDEFINED.Fix
JSValue::NULL.callback(null, buf)on success (lib/internal/crypto/scrypt.js:59at v26.3.0). The failure path already passes the error alone, like Node.test/js/node/crypto/scrypt.test.ts(one new test, it fails on Bun 1.4.3 and on a debug build of main). Alsotest/js/node/test/parallel/test-crypto-scrypt.jsand thecrypto-scryptcase ofAsyncLocalStorage-tracking.test.ts.Background
crypto.scryptis a native function with no JS wrapper.ScryptJobderives the key on the work pool. ThenScryptJob::thenruns on the JS thread and calls the user callback.ScryptJob::thenis the only code that builds this argument list, so the fix has one possible place. A JS wrapper that mapsundefinedtonull(the shapecrypto.argon2has) adds a closure per call.node:cryptocallbacks already match Node. 18 more calls give the same first argument in Bun 1.4.3 and Node v26.3.0 (table in Notes).Downsides
errwithundefinedaftercrypto.scryptnow seesnull. That code already fails on Node.Notes
Reproduction (exit 1 on Bun 1.4.3, exit 0 on Node v26.3.0 and with this change):
"Bun 1.4.3" in this text is the build
1.4.3-canary.1+367d939d9.First callback argument on success, Bun 1.4.3 compared with Node v26.3.0:
scrypt(no options, options,keylen0)nullundefinedpbkdf2,hkdf,randomBytes,randomFill(also with size 0)nullnullgenerateKey(hmac, aes)nullnullgenerateKeyPair(ed25519, x25519, ec, rsa)nullnullsign,verify,diffieHellman,argon2nullnullrandomInt,generatePrime,checkPrimeundefinedundefinedNode passes
undefinedfor the last row, so there is no single rule for every callback. Each call keeps its own constant.crypto.argon2reaches the user through a JS wrapper insrc/js/node/crypto.tsthat maps the nativeundefinedtonull. That path is correct and this change does not touch it.The value passed as
derivedKeyis the sameBufferas before. The failure path (callback(err), one argument) does not change.Suites run with the debug build:
test/js/node/crypto/scrypt.test.ts: 3 pass. Without thesrc/change the new test fails with- null/+ undefinedfor both calls.test/js/node/test/parallel/test-crypto-scrypt.js: exit 0.test/js/node/async_hooks/AsyncLocalStorage-tracking.test.ts -t scrypt: 1 pass.[auto-merge] gate passed · iteration 0 · 2 files touched
fails on main (without fix)
passes on PR (with fix)
diff hotspot
gate history · 1 passed · 0 rejected · iteration 0
evidence per changed file