-
Notifications
You must be signed in to change notification settings - Fork 25
Description
[bug] Monitor 'error' 事件在无监听器时触发 Node.js ERR_UNHANDLED_ERROR
报告人 / Reporter
- GitHub 用户名(期望):
2217173240 - 邮箱:
alanturing648@gmail.com
环境 (Environment)
@shareai-lab/kode-sdk版本:2.7.0(external/Kode-agent-sdk/package.json)- Node.js:
>= 18
背景 (Background)
Kode SDK 的错误处理文档 docs/ERROR_HANDLING.md 中的目标之一是:
- 程序永不崩溃 - 多层错误捕获,确保系统稳定运行
工具执行失败时,Agent 会发出 MonitorEvent,其中一种类型是:
// external/Kode-agent-sdk/src/core/agent.ts:1231+
this.events.emitMonitor({
channel: 'monitor',
type: 'error',
severity: 'warn',
phase: 'tool',
message: errorMessage,
detail: { ...outcome.content, errorType, retryable: isRetryable },
});EventBus 在发出 Monitor 事件时,会直接用 event.type 作为 Node.js EventEmitter 的 event name:
// external/Kode-agent-sdk/src/core/events.ts:58-66
emitMonitor(event: MonitorEvent): AgentEventEnvelope<MonitorEvent> {
const envelope = this.emit('monitor', event) as AgentEventEnvelope<MonitorEvent>;
this.monitorEmitter.emit(event.type, envelope.event);
this.notifySubscribers('monitor', envelope);
return envelope;
}当 event.type === 'error' 且未注册任何 'error' 监听器时,Node.js 会将其视为“未处理的 error 事件”,触发 ERR_UNHANDLED_ERROR,直接导致进程崩溃。
复现步骤 (Steps to Reproduce)
-
在 Node.js 中创建最小化环境,只使用 Kode SDK,不注册任何
error监听器:import { Agent, JSONStore, AgentTemplateRegistry, ToolRegistry, SandboxFactory, } from '@shareai-lab/kode-sdk'; const store = new JSONStore('./.kode'); const templates = new AgentTemplateRegistry(); const tools = new ToolRegistry(); const sandboxFactory = new SandboxFactory(); // 注册一个必然失败的工具 tools.register('failing_tool', () => ({ name: 'failing_tool', description: 'Always fail', async exec() { throw new Error('intentional failure'); // 或者: // return { ok: false, error: 'intentional failure' }; }, })); templates.register({ id: 'test-agent', systemPrompt: 'test', tools: ['failing_tool'], model: 'dummy-model', }); (async () => { const agent = await Agent.create( { agentId: 'agt_test', templateId: 'test-agent', model: {} as any, // stub ModelProvider }, { store, templateRegistry: templates, toolRegistry: tools, sandboxFactory, } ); // 注意:不注册 agent.on('error', ...) await agent.complete([ { role: 'user', content: [{ type: 'text', text: 'trigger failing_tool' }] } as any, ]); })();
-
工具执行失败后,
processToolCall会调用this.events.emitMonitor({ type: 'error', ... })。 -
EventBus.emitMonitor再执行this.monitorEmitter.emit('error', envelope.event)。 -
因为未注册任何
'error'监听器,Node.js 抛出ERR_UNHANDLED_ERROR: Unhandled 'error' event,进程直接退出。
实际行为 (Actual Behavior)
- 未注册
agent.on('error', handler)的情况下,任何工具失败都可能触发monitorEmitter.emit('error', ...),并在 Node.js 中引发ERR_UNHANDLED_ERROR,导致进程崩溃。 - 这与文档中“程序永不崩溃”的设计目标不一致,对集成方来说是一个隐藏的 footgun(必须知道要显式注册
'error',否则进程会挂)。
预期行为 (Expected Behavior)
- 即使集成方没有注册
agent.on('error', ...),SDK 仍应保证:- 进程不会因为
MonitorEvent.type === 'error'而触发 Node 层的ERR_UNHANDLED_ERROR; - 错误可以通过其他方式(如
agent.subscribe([...])、Store、日志)被观察和处理。
- 进程不会因为
- 注册
agent.on('error', handler)应该是“增强监控”的选项,而不是“避免进程崩溃的硬性前提”。
影响范围 (Impact)
- 所有没有显式注册
agent.on('error', ...)的集成方,只要工具执行失败,就有可能因为 monitor'error'事件导致主进程崩溃。 - 在实际业务项目中(商用车资料检索 Agent),早期版本就出现过“工具逻辑错误 → MonitorEvent 'error' → 进程崩溃”的链路,目前只能通过在业务层统一注册
agent.on('error', ...)来规避。
建议修复方向 (Suggested Fix)
-
避免使用
'error'作为 Node EventEmitter 的 event name,例如在EventBus.emitMonitor中统一使用'monitor_error'作为 event name,将MonitorEvent.type保持为'error'即可。 -
或者在
EventBus构造函数中为monitorEmitter预先注册一个空的'error'监听器,屏蔽 Node 的特殊处理语义:this.monitorEmitter.on('error', () => { // no-op or re-route to logging });
-
同时在文档中明确说明:
agent.on('error', ...)是推荐的监控方式;- 即使没有注册该监听器,SDK 也不会因为工具失败而让进程崩掉。