Skip to content

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

@karlhorky

Description

@karlhorky

Companion issue (expo/expo): expo/expo#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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions