Skip to content

[Bug]: Extensions using CJS dependencies fail with "require is not defined" #12854

@lukeboyett

Description

@lukeboyett

Bug: Extensions using CJS dependencies fail with "require is not defined"

Description

Extensions loaded via jiti that depend on CommonJS npm packages fail at runtime with ReferenceError: require is not defined. This affects any extension whose dependency tree includes CJS modules that call require() internally.

In my case, the Matrix extension depends on @vector-im/matrix-bot-sdk (a CJS package). When the message tool triggers a send, the bot SDK's internal require("events"), require("htmlencode"), etc. fail because jiti evaluates the extension in an ESM context where require is not a global.

Environment

  • OpenClaw: 2026.2.9
  • Node: v25.5.0
  • OS: macOS (Darwin 23.6.0, x64)
  • Extension: Matrix (@vector-im/matrix-bot-sdk v0.7.1)

Steps to Reproduce

  1. Install OpenClaw with the Matrix extension enabled
  2. Configure Matrix channel with a valid homeserver/token
  3. Call the message tool with action: "send" targeting a Matrix room
  4. Result: require is not defined

dryRun: true succeeds (doesn't enter the plugin send path), confirming the error occurs only when the CJS dependency is actually invoked at runtime.

Expected Behavior

The message should send successfully. CJS dependencies of extensions should work regardless of jiti's ESM transpilation.

Actual Behavior

[tools] message failed: require is not defined

The error is a bare ReferenceError with no stack trace (it gets stringified to just the message before reaching the tool error handler).

Root Cause

In src/plugins/loader.ts, extensions are loaded via jiti:

const jiti = createJiti(import.meta.url, {
  interopDefault: true,
  extensions: [".ts", ".tsx", ".mts", ".cts", ".mtsx", ".ctsx", ".js", ".mjs", ".cjs", ".json"],
});
// ...
mod = jiti(candidate.source) as OpenClawPluginModule;

jiti transpiles TypeScript extensions to ESM. When these extensions import from CJS packages (like @vector-im/matrix-bot-sdk), jiti handles the initial import. However, when the CJS package's own internal code calls require() at runtime, that call happens in a context where require is not defined — because the parent ESM context doesn't provide it.

Workaround

Create a preload shim that injects require globally:

require-shim.mjs:

import { createRequire } from "node:module";
if (!globalThis.require) {
  globalThis.require = createRequire(import.meta.url);
}

Apply via:

NODE_OPTIONS="--import ./require-shim.mjs" openclaw gateway

Or in a launchd plist, add to EnvironmentVariables:

<key>NODE_OPTIONS</key>
<string>--import /path/to/require-shim.mjs</string>

Proposed Fix

Any of these approaches would fix it properly:

Option A: Inject require in the plugin loader (minimal change)

In src/plugins/loader.ts, before loading extensions:

import { createRequire } from "node:module";
if (!globalThis.require) {
  globalThis.require = createRequire(import.meta.url);
}

Option B: Configure jiti to handle CJS natively

Pass appropriate options to createJiti so that CJS dependencies are delegated to Node's native CJS loader rather than being transpiled.

Option C: Use Node's native TypeScript support

Node 25+ supports --experimental-strip-types. Extensions could be loaded via native import() instead of jiti, which would preserve proper CJS interop automatically.

Impact

This affects any extension that depends on CJS npm packages — not just Matrix. It's been present since at least v2026.2.4 and likely earlier. Every message tool call fails, making the Matrix extension effectively non-functional for sending messages without the workaround.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingstaleMarked as stale due to inactivity

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions