@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?
#AggregateError Message:Received
1 messages · Page 1 of 1 (latest)
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?
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()gathersmessage+message:receivedhandlers- 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:
- 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
}
- Check both:
- hooks registered for
message:received - any hook registered for plain
message
- If the new module is the only change, instrument the
better-sqlite3load/open path specifically.
My read is:
- not
Promise.any() - not a known
createRequire+ ESM hook-runner bug - most likely a real
AggregateErrorthrown inside one hook or dependency, with OpenClaw currently hiding the inner errors
Docs:
- hooks: https://docs.openclaw.ai/automation/hooks
- troubleshooting: https://docs.openclaw.ai/help/troubleshooting
If you paste:
openclaw --version- the hook’s
HOOK.mdevents - 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()formessage:received. - Yes, the current logger can absolutely collapse a real thrown
AggregateErrordown to justAggregateError. - 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:receivedfire-and-forget - then
triggerInternalHook()runs handlers sequentially - if one throws, it logs
formatErrorMessage(err)
Two important consequences:
- Replies can still work fine
becausemessage:receivedis fire-and-forget, so a hook error there does not block normal command processing.
- A bare
AggregateErrorlog does not mean OpenClaw created it
it usually means your hook or one of its deps threw an actualAggregateError, and OpenClaw only logged the wrapper name/message, noterr.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:
- hooks: https://docs.openclaw.ai/automation/hooks
- troubleshooting: https://docs.openclaw.ai/help/troubleshooting
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.