#AggregateError Message:Received

1 messages · Page 1 of 1 (latest)

hearty copper
#

@frank belfry Hey — running OpenClaw with workspace hooks. After deploying a new workspace module (lib/db.mjs) that loads better-sqlite3 via createRequire(import.meta.url), I'm seeing [internal-hooks] Hook error [message:received]: AggregateError in the log on every user message. The hooks themselves work correctly — commands return proper responses. The error fires exactly once per message. It didn't appear before this deploy. I've reviewed all 9 hooks and none of them are throwing. Does OpenClaw's hook runner use Promise.any() for collecting message:received results, and if so, is there a way to get the individual errors inside the AggregateError rather than just the wrapper? Or is there a known interaction between native modules loaded via createRequire in ESM workspace hooks and the hook runner?

paper cosmosBOT
hearty copper
#

AggregateError on every message:received after deploying native module (better-sqlite3) in workspace hook
Goal / expected behaviour:
Deployed a new shared module (lib/db.mjs) in my workspace that loads better-sqlite3 via createRequire(import.meta.url). Hooks that import this module work correctly — commands return proper responses. Expected: no errors logged.
What happened instead:
[internal-hooks] Hook error [message:received]: AggregateError appears in the gateway log on every user message. Fires exactly once per message. Did not appear before this deploy.
What I ran:
Added lib/db.mjs to the workspace (ESM, uses createRequire to load better-sqlite3). Updated projects.mjs and cost-tracker.mjs to import from it. Restarted gateway.
Environment:

OS: Windows 11
OpenClaw: 2026.4.15 (041266a)
Node: 24.13.1
Install method: npm global (npm install -g openclaw)
better-sqlite3: 11.x, resolves correctly (migration script ran clean, hooks return correct data)

Relevant log snippet:
12:11:16 [docx-ingest] Slash command detected, skipping
12:11:17 [internal-hooks] Hook error [message:received]: AggregateError
12:11:45 [internal-hooks] Hook error [message:received]: AggregateError
12:11:45 [docx-ingest] Slash command detected, skipping
Full log entry (JSON):
json{"0":"{"subsystem":"internal-hooks"}","1":"Hook error [message:received]: AggregateError","_meta":{"path":{"fullFilePath":"file:///C:/Users/jason/AppData/Roaming/npm/node_modules/openclaw/dist/subsystem-Cgmckbux.js:324:14"},"logLevelName":"ERROR","date":"2026-04-19T16:11:17.501Z"}}
Question:
Does the hook runner use Promise.any() to collect message:received results? Is there a way to surface the individual errors inside the AggregateError beyond what's logged? And is there a known issue with native modules loaded via createRequire in ESM workspace hooks interacting with the hook runner?

frank belfryBOT
#

Let me check the local docs and the hook runner code directly.

#

I found the hook runtime. It does not use Promise.any() there, so that part already looks suspicious. I'm checking where message:received is emitted and whether the logged AggregateError is just the error object your hook threw.

#

One important bit: the current logger only prints err.message || err.name, so if a hook throws a bare AggregateError with no message, OpenClaw will log exactly AggregateError and hide error.errors[]. That matches what you're seeing a little too well.

#

First, what does openclaw --version print?

Provisional answer though: on current OpenClaw ref, the internal hook runner does not use Promise.any() for message:received.

What it does:

  • triggerInternalHook() gathers message + message:received handlers
  • runs them sequentially
  • catches each handler error individually
  • logs formatErrorMessage(err)

So if your log is just:

[internal-hooks] Hook error [message:received]: AggregateError

that usually means the handler/dependency threw an AggregateError, and OpenClaw only logged the wrapper name/message, not error.errors[].

Also, createRequire(import.meta.url) in ESM is not inherently suspicious here. OpenClaw itself uses that pattern in multiple places, so I don’t see a known hook-runner incompatibility with it.

#

What I’d check next:

  1. Wrap the hook body, or just the DB init/call, and log the raw error:
try {
  await doThing()
} catch (err) {
  console.error("raw hook error", err)
  console.error("cause", err?.cause)
  console.error("errors", err instanceof AggregateError ? err.errors : undefined)
  throw err
}
  1. Check both:
  • hooks registered for message:received
  • any hook registered for plain message
#
  1. If the new module is the only change, instrument the better-sqlite3 load/open path specifically.

My read is:

  • not Promise.any()
  • not a known createRequire + ESM hook-runner bug
  • most likely a real AggregateError thrown inside one hook or dependency, with OpenClaw currently hiding the inner errors

Docs:

If you paste:

  • openclaw --version
  • the hook’s HOOK.md events
  • and one wrapped raw error dump

I can help narrow it down fast.

#

Nice, that extra detail helps.

Short answer:

  • No, the internal hook runner is not using Promise.any() for message:received.
  • Yes, the current logger can absolutely collapse a real thrown AggregateError down to just AggregateError.
  • No known generic issue with createRequire(import.meta.url) in ESM workspace hooks. OpenClaw itself uses that pattern in a bunch of places.

What the runtime is doing right now:

  • inbound message bridge fires message:received fire-and-forget
  • then triggerInternalHook() runs handlers sequentially
  • if one throws, it logs formatErrorMessage(err)

Two important consequences:

  1. Replies can still work fine
    because message:received is fire-and-forget, so a hook error there does not block normal command processing.
#
  1. A bare AggregateError log does not mean OpenClaw created it
    it usually means your hook or one of its deps threw an actual AggregateError, and OpenClaw only logged the wrapper name/message, not err.errors.

So my read is:

  • the runner is not the source of the AggregateError
  • one hook is likely throwing it on every inbound message
  • since it happens once per message, it’s probably exactly one handler bound to either:
    • message:received, or
    • plain message

Also, if better-sqlite3 were fundamentally failing to load, I’d expect a more obvious native-module error (MODULE_NOT_FOUND, DLL/ABI load failure, etc.) and probably broken behavior, not “works correctly but logs one AggregateError per inbound”.

What to do next:

try {
#
  await doTheMessageHookStuff()
} catch (err) {
  console.error("raw hook error:", err)
  console.error("cause:", err?.cause)
  if (err instanceof AggregateError) {
    for (const [i, sub] of err.errors.entries()) {
      console.error(`AggregateError[${i}]`, sub)
    }
  }
  throw err
}

I’d put that around:

  • the DB open/init path
  • the first query call
#
  • any helper that fans out async work

Also check for any hook registered on plain message, not just message:received.

Docs:

If you paste the HOOK.md event list for the affected hooks, plus the lib/db.mjs init/open code, I can probably narrow it down pretty fast.

Glad that helped! 🎉
If this fixed it, please right-click the solution message → Apps → ✅ Mark Solution.
This helps Answer Overflow index the solution for future users.