Bug: diagnostics-otel plugin doesn't receive events due to module isolation
Summary
The diagnostics-otel plugin loads successfully and subscribes to diagnostic events via onDiagnosticEvent(), but never receives any events because the plugin's module instance is isolated from the gateway's module instance.
Environment
- Clawdbot version: 2026.1.24-3
- Node.js: v25.4.0
- OS: macOS (Darwin arm64)
Steps to Reproduce
- Enable diagnostics in config:
{
"diagnostics": {
"enabled": true,
"otel": {
"enabled": true,
"endpoint": "http://localhost:4318",
"protocol": "http/protobuf",
"serviceName": "milo",
"traces": true,
"metrics": true
}
}
}
- Add
diagnostics-otel to plugins allow list and entries
- Start OTel collector on localhost:4318
- Restart gateway
- Observe that no metrics are ever sent to collector
Debug Findings
Added debug logging to dist/infra/diagnostic-events.js:
export function onDiagnosticEvent(listener) {
console.error('[DEBUG] Adding listener. Current count:', listeners.size);
listeners.add(listener);
console.error('[DEBUG] After add:', listeners.size);
// ...
}
export function emitDiagnosticEvent(event) {
console.error('[DEBUG] Emitting, listeners:', listeners.size);
// ...
}
Results:
- When plugin calls
onDiagnosticEvent(): listener count goes 0 → 1 ✓
- When gateway calls
emitDiagnosticEvent(): listener count is 0 ✗
This proves the plugin and gateway are using different module instances of diagnostic-events.js.
Root Cause
The plugin is loaded via jiti (TypeScript loader), which appears to create a separate module cache. When the plugin imports onDiagnosticEvent from clawdbot/plugin-sdk, it gets a different instance of the listeners Set than the one used by the gateway's emitDiagnosticEvent.
Expected Behavior
The plugin should receive diagnostic events and export them to the OTel collector.
Actual Behavior
Plugin loads, SDK starts, subscribes to events, but never receives any because module isolation breaks the shared event emitter.
Possible Fixes
- Use a global registry (e.g.,
globalThis.__clawdbot_diagnostic_listeners__) instead of module-level variable
- Pass the event emitter through the plugin context
- Ensure jiti shares the module cache with the main process
Workaround
None known - core code change required.
Bug: diagnostics-otel plugin doesn't receive events due to module isolation
Summary
The
diagnostics-otelplugin loads successfully and subscribes to diagnostic events viaonDiagnosticEvent(), but never receives any events because the plugin's module instance is isolated from the gateway's module instance.Environment
Steps to Reproduce
{ "diagnostics": { "enabled": true, "otel": { "enabled": true, "endpoint": "http://localhost:4318", "protocol": "http/protobuf", "serviceName": "milo", "traces": true, "metrics": true } } }diagnostics-otelto plugins allow list and entriesDebug Findings
Added debug logging to
dist/infra/diagnostic-events.js:Results:
onDiagnosticEvent(): listener count goes 0 → 1 ✓emitDiagnosticEvent(): listener count is 0 ✗This proves the plugin and gateway are using different module instances of
diagnostic-events.js.Root Cause
The plugin is loaded via jiti (TypeScript loader), which appears to create a separate module cache. When the plugin imports
onDiagnosticEventfromclawdbot/plugin-sdk, it gets a different instance of thelistenersSet than the one used by the gateway'semitDiagnosticEvent.Expected Behavior
The plugin should receive diagnostic events and export them to the OTel collector.
Actual Behavior
Plugin loads, SDK starts, subscribes to events, but never receives any because module isolation breaks the shared event emitter.
Possible Fixes
globalThis.__clawdbot_diagnostic_listeners__) instead of module-level variableWorkaround
None known - core code change required.