Treat a cancelled SSE stream as aborted
ci / vet-and-test (pull_request) Successful in 48s
ci / docker-checkout-hostgateway (pull_request) Successful in 1s

Why: the race detector can deliver message_end when the context is already cancelled, because select picks either ready case.
Evidence: GOWORK=off go test -count=50 -race ./runtime/llm/ passed, including TestParseSSECancel.
Impact: a cancelled parse ends with the aborted event.
Refs: D1
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-03 13:51:41 +00:00
co-authored by Cursor
parent 33573d0d75
commit d84a67cd24
+6
View File
@@ -151,6 +151,12 @@ func emitParsed(ctx context.Context, r io.Reader, out chan<- Event) {
emitError(out, content.String(), true, err.Error())
return
}
// 取消必须压过正常结束。select 在两个分支都就绪时随机挑选,
// 竞态下会把 message_end 发给已经取消的调用方。
if ctx.Err() != nil {
emitAbort(out, content.String(), ctx.Err())
return
}
msg := Message{Role: RoleAssistant, Content: content.String(), StopReason: mapFinish(finished, len(order) > 0)}
for _, idx := range order {
msg.ToolCalls = append(msg.ToolCalls, *calls[idx])