Unhandled "provider is closed" inside the runtime's own error handler terminates the host process
- 主要语言
- Java
- 星标
- 10.5k
- 派生
- 1.5k
- 平均合并
- 1 天 14 小时
- 30 天内合并 PR
- 129
描述
**Environment:** `@github/copilot` 1.0.71 (win32-x64), Node v24.16.0, .NET 8 host embedding the SDK in-process (napi-oop-runtime).
## Summary
When a tool-call result is written to a session after its provider has been closed, the runtime raises `Error: provider is closed`. Its error handler then logs that failure *through the same closed provider*, which throws a second time inside the handler. That second throw is unhandled and terminates the host process with `0xC0000005` (access violation) rather than failing the operation.
```
Error: provider is closed
at Object.call (napi-oop-runtime/dist/index.js:68:2208)
at Proxy.S (napi-oop-runtime/dist/index.js:68:3671)
at t.filterSecrets (app.js:114:26376)
at wR.filterSecrets (app.js:143:52707)
at wR.error (app.js:651:10732)
at wR.writeLog (app.js:652:163)
at Hle.logToLevel (app.js:61:1536)
at Hle.error (app.js:61:1719)
at t.handleError (app.js:5076:625)
at process. (app.js:5076:360)
```
The final two frames are the process-level handler, so there is nothing left to catch it.
## Reproduction shape
1. Create a session and subscribe to `ExternalToolRequestedEvent`.
2. Dispatch the tool asynchronously — the event handler cannot await it, so the dispatch is fire-and-forget.
3. Dispose the session while a dispatch is still in flight, for example because the turn was cancelled by a deadline.
4. The dispatch completes and calls `session.Rpc.Tools.HandlePendingToolCallAsync(...)` on the now-closed session.
Observed reliably on a long-running host that creates one session per turn against a process-wide client.
## Impact
The whole host process dies, not just the affected session. For a service embedding the SDK, every concurrent operation in that process is lost, and the failure surfaces as a process exit rather than an exception the caller can handle. A caller cannot defend against this in managed code either, since an access violation is not catchable.
## Suggested fix
Make the error-handling path resilient to a closed provider. `filterSecrets` / `writeLog` reach back into the provider while handling an error that the provider itself raised; falling back to a local sink when the provider is unavailable would keep the secondary failure inside `handleError` instead of letting it escape.
More generally, an error raised because a resource is closed should not be reported through that same resource.
## Workaround
On the caller side: gate any post-dispatch write on session liveness, and drain in-flight dispatches before disposing the session. That removes the trigger but not the underlying fragility — any other error raised after provider close will still terminate the process.
贡献指南
调研方向
从 dist/index.js 中显示的 napi-oop-runtime 错误路径以及 app.js 中的 filterSecrets、writeLog 和 handleError 帧开始;重现一次正在进行的工具 dispatch,随后释放 session。完成标准是:关闭后的失败会被报告,且不会递归使用已关闭的 provider,同时 host 仍保持运行,并产生一个可捕获的操作失败。
由索引模型根据 Issue 内容生成。
评估
- 技术栈
- csharp, node.js
- 领域
- api, backend
- Issue 类型
- 缺陷
- 难度
- 4/5
- 预计耗时
- 3-5 天
- 活跃度
- 活跃
- 描述清晰度
- 基本清楚
- 新手友好度
- 48/100