napi-rs / napi-rs/node-rs

"Failed to load native binding" with `@node-rs/bcrypt` + Expo Router API Routes (Metro bundler)

Open
#911 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
1.5k
Forks
52
Avg merge
13h 37m
Merged PRs (30d)
19

Description

Companion issue (expo/expo): https://github.com/expo/expo/issues/32350

Hi, first of all, thanks for these Rust libraries! Nice performance, and good options when the main bcrypt, etc packages don't work in a particular environment.

We are trying to use @node-rs/bcrypt in Expo Router API Routes (using Metro bundler), and we're getting some output that the native binding cannot be loaded (because it's "undefined", see further below):

Android Bundled 852ms node_modules/expo-router/entry.js (1049 modules)
λ Bundled 443ms app/hash+api.ts (4 modules)

Metro error: Failed to load native binding

  357 |     //  - The user may need to bundle the correct files
  358 |     //  - The user may need to re-install node_modules to get new packages
> 359 |     throw new Error('Failed to load native binding', { cause: loadErrors })
      |           ^
  360 |   }
  361 |   throw new Error(`Failed to load native binding`)
  362 | }

Call Stack
  factory (node_modules/@node-rs/bcrypt/binding.js:359:11)
  loadModuleImplementation (node_modules/metro-runtime/src/polyfills/require.js:277:5)
  guardedLoadModule (node_modules/metro-runtime/src/polyfills/require.js:184:12)
  require (node_modules/metro-runtime/src/polyfills/require.js:92:7)
  factory (node_modules/@node-rs/bcrypt/index.js:1:84)
  loadModuleImplementation (node_modules/metro-runtime/src/polyfills/require.js:277:5)
  guardedLoadModule (node_modules/metro-runtime/src/polyfills/require.js:184:12)
  require (node_modules/metro-runtime/src/polyfills/require.js:92:7)
  factory (app/hash+api.ts:1)
  loadModuleImplementation (node_modules/metro-runtime/src/polyfills/require.js:277:5)
 ERROR  [SyntaxError: JSON Parse error: Unexpected character: <]

Screenshot 2024-10-25 at 15 43 50

I think Metro is not logging out the error cause, so adapting the node_modules/@node-rs/bcrypt/binding.js to log out loadErrors shows that it is using "undefined" as the module specifier:

if (!nativeBinding) {
  if (loadErrors.length > 0) {
    // TODO Link to documentation with potential fixes
    //  - The package owner could build/publish bindings for this arch
    //  - The user may need to bundle the correct files
    //  - The user may need to re-install node_modules to get new packages
+   console.log(loadErrors[0])
+   console.log(loadErrors[1])
+   console.log(loadErrors[2])
+   console.log(loadErrors[3])
    throw new Error('Failed to load native binding', { cause: loadErrors })
  }
  throw new Error(`Failed to load native binding`)
}

This is repeated 4 times:

λ  LOG  Error: Requiring unknown module "undefined". If you are sure the module exists, try restarting Metro. You may also want to run `yarn` or `npm install`.
    at unknownModuleError (/Users/k/p/repro-expo-router-api-routes-node-rs-bcrypt/node_modules/metro-runtime/src/polyfills/require.js:322:10)
    at loadModuleImplementation (/Users/k/p/repro-expo-router-api-routes-node-rs-bcrypt/node_modules/metro-runtime/src/polyfills/require.js:244:11)
    at guardedLoadModule (/Users/k/p/repro-expo-router-api-routes-node-rs-bcrypt/node_modules/metro-runtime/src/polyfills/require.js:184:12)
    at require (/Users/k/p/repro-expo-router-api-routes-node-rs-bcrypt/node_modules/metro-runtime/src/polyfills/require.js:92:7)
    at requireNative (/Users/k/p/repro-expo-router-api-routes-node-rs-bcrypt/node_modules/@node-rs/bcrypt/binding.js:130:16)
    at factory (/Users/k/p/repro-expo-router-api-routes-node-rs-bcrypt/node_modules/@node-rs/bcrypt/binding.js:332:17)
    at loadModuleImplementation (/Users/k/p/repro-expo-router-api-routes-node-rs-bcrypt/node_modules/metro-runtime/src/polyfills/require.js:277:5)
    at guardedLoadModule (/Users/k/p/repro-expo-router-api-routes-node-rs-bcrypt/node_modules/metro-runtime/src/polyfills/require.js:184:12)
    at require (/Users/k/p/repro-expo-router-api-routes-node-rs-bcrypt/node_modules/metro-runtime/src/polyfills/require.js:92:7)
    at /Users/k/p/repro-expo-router-api-routes-node-rs-bcrypt/node_modules/@node-rs/bcrypt/index.js:1:84

Reproduction

Creation steps:

  1. mkdir repro-expo-router-api-routes-node-rs-bcrypt && cd repro-expo-router-api-routes-node-rs-bcrypt
  2. npx create-expo-app@latest .
  3. rm -r ./node_modules && npm install (for EMFILE error)
  4. Remove extra files
  5. Configure app.json with expo.web.output = "server" and expo.plugins[0][1] = { "origin": "http://localhost:8081" }
  6. Create a file app/hash+api.ts, with an Expo Router API Route, using hashSync from @node-rs/bcrypt
  7. Inside app/(tabs)/index.tsx, add a fetch() of the API Route inside useFocusEffect
  8. 💥 npm start and observe the error after API Route bundling

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with packages/bcrypt/binding.js, especially requireNative and the native binding loading path, then compare it with the Metro reproduction in app/hash+api.ts and the Expo Router setup. Reproduce the failure with the linked project and inspect how Metro resolves the binding candidates. Done means the API route bundles and hashSync runs without the undefined-module or failed-native-binding errors.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, nodejs, rust
Domain
backend, build-system
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.