Skip to content

πŸ› Bug Report β€” Runtime APIs: require("node:process") fails with "No such module" while ESM import succeedsΒ #7978

Closed

Description

@k-taro56

Summary

With nodejs_compat enabled, both require("process") and require("node:process") throw:

Error: No such module "node:process".

This occurs with both:

  • require() inside a commonJsModule.
  • The require function returned by createRequire() from node:module.

The thrown error has no code property (e.code === undefined).

However, the equivalent ES module import works:

import process from "node:process";

The imported object is identical to globalThis.process.

Other Node.js builtins tested resolve successfully through require(), including:

util, perf_hooks, path, module, url, worker_threads, fs, os, crypto, tty, events, async_hooks, node:http, node:https, and child_process.

Adding either enable_nodejs_process_v2 or disable_nodejs_process_v2 to the compatibility flags does not change the behavior.

Expected behavior

Both require("process") and require("node:process") should return the same object as:

import process from "node:process";

That object should also be identical to globalThis.process, consistent with Node.js behavior and with how other builtins resolve in workerd.

If a builtin cannot be loaded through require(), the resulting error should at least have a meaningful code property, such as MODULE_NOT_FOUND or ERR_UNKNOWN_BUILTIN_MODULE, so callers can identify and handle the failure.

Impact

Libraries that load process through require() during module evaluation fail to initialize.

One affected example is the Node.js build of @vercel/otel (version 2.1.3).

In a Next.js application:

  1. Next.js bundles @vercel/otel into the application's instrumentation.ts.
  2. During module evaluation, the library's host detector calls createRequire(import.meta.url)("process").
  3. This throws because process cannot be resolved through require().
  4. The OpenTelemetry initialization fails, and Next.js propagates the error from its instrumentation hook without an error code.
  5. Consequently, every request to the affected application fails with HTTP 500.

This makes the issue more than a compatibility discrepancy: it can prevent an otherwise functioning application from serving requests.

Reproduction

Environment

  • Runtime: workerd 1.20260917.1
  • Package: npm workerd
  • Platform: darwin-arm64
  • Compatibility date: 2026-09-15
  • Compatibility flags: ["nodejs_compat"]

Create the following three files.

config.capnp

using Workerd = import "/workerd/workerd.capnp";
const config :Workerd.Config = (
  services = [ (name = "main", worker = .w) ],
  sockets = [ (name = "http", address = "127.0.0.1:18790", http = (), service = "main") ],
);
const w :Workerd.Worker = (
  modules = [
    (name = "index.mjs", esModule = embed "index.mjs"),
    (name = "app.cjs", commonJsModule = embed "app.cjs"),
  ],
  compatibilityDate = "2026-09-15",
  compatibilityFlags = ["nodejs_compat"],
);

index.mjs

import app from './app.cjs';
import esmProcess from 'node:process';

export default {
  async fetch() {
    return Response.json({
      esmImport: typeof esmProcess + ':same-as-global=' + (esmProcess === globalThis.process),
      cjs: app.run(),
    });
  },
};

app.cjs

module.exports.run = function run() {
  const out = {};

  for (const name of ['process', 'node:process', 'fs']) {
    try {
      out[`require(${name})`] = 'ok:' + typeof require(name);
    } catch (e) {
      out[`require(${name})`] = `throws ${e.message} (code: ${e.code})`;
    }
  }

  const created = require('node:module').createRequire('file:///bundle/index.js');

  for (const name of ['process', 'node:process', 'fs']) {
    try {
      out[`createRequire(${name})`] = 'ok:' + typeof created(name);
    } catch (e) {
      out[`createRequire(${name})`] = `throws ${e.message} (code: ${e.code})`;
    }
  }

  return out;
};

Run

Start workerd:

npx workerd serve config.capnp

Then send a request:

curl http://127.0.0.1:18790/

Actual output

{
  "esmImport": "object:same-as-global=true",
  "cjs": {
    "require(process)": "throws No such module \"node:process\". (code: undefined)",
    "require(node:process)": "throws No such module \"node:process\". (code: undefined)",
    "require(fs)": "ok:object",
    "createRequire(process)": "throws No such module \"node:process\". (code: undefined)",
    "createRequire(node:process)": "throws No such module \"node:process\". (code: undefined)",
    "createRequire(fs)": "ok:object"
  }
}

The output confirms that:

  • node:process resolves correctly through ESM import and is identical to globalThis.process.
  • Both process and node:process fail through require() and createRequire().
  • Another builtin (fs) resolves successfully through both mechanisms.
  • The errors have no code property.

The same failure occurs when either enable_nodejs_process_v2 or disable_nodejs_process_v2 is added to the compatibility flags.

Workaround

As a temporary workaround, we wrap createRequire() so that the returned require function:

  • Returns globalThis.process for "process" and "node:process".
  • Delegates all other module specifiers to the original require function.

This avoids the failure in the affected library, but requires patching module resolution behavior that should work natively under nodejs_compat.

Activity

  1. kevinlupera commented on Oct 26, 2026

    @kevinlupera
    Contributor

    I would like to work on this.

    From reading the source, the cause looks clear. node:process has no registration of its own. It has to be redirected to node-internal:public_process or node-internal:legacy_process, depending on enable_nodejs_process_v2. On the legacy module registry, that redirect happens for static imports (jsg/modules.c++, resolveCallback) and for dynamic imports (jsg/modules.h). It is missing from both require() paths:

    • CommonJsModuleContext::require() in api/commonjs.c++
    • ModuleUtil::createRequire() in api/node/module.c++

    Both end in No such module "node:process"., which matches the report.

    Plan: apply the same redirect in both paths, and add tests for require("process") and require("node:process") with the flag on and off. The tests will check that the result is identical to globalThis.process. I would leave the missing code on the error for a separate change, unless you prefer it here.

  2. added a commit that references this issue on Oct 28, 2026
    d299c17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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