Skip to content

fix(spray): guard emitStats against cancelled context#21

Open
wuchulonly wants to merge 2 commits into
masterfrom
fix/emitstats-ctx-guard
Open

fix(spray): guard emitStats against cancelled context#21
wuchulonly wants to merge 2 commits into
masterfrom
fix/emitstats-ctx-guard

Conversation

@wuchulonly

Copy link
Copy Markdown
Contributor

Summary

  • Fix send on closed channel panic when SprayEngine.execute's deferred emitStats fires after the consumer pipeline has shut down
  • Root cause: execute() calls ctx.emitStats() in a defer. When the caller's context times out, the consumer pipeline closes its event channel before the defer fires. The OnStats callback then chains back into p.submit() → send on closed channel → panic
  • Fix: check ctx.Err() in emitStats() before invoking the callback — cancelled context means the consumer is gone

Observed in

aiscan hw2026 w3 node — panic: send on closed channel at pipeline.go:214, goroutine 29767, call chain: execute defer → emitStats → OnStats → emit → p.submit

Changes

  • spray/types.go: add c.ctx.Err() != nil guard in emitStats (1 line change)
  • spray/context_test.go: 4 unit tests covering nil context, nil handler, cancelled context, active context

Test plan

  • go test ./spray/... -run TestEmitStats — all 4 pass

🤖 Generated with Claude Code

root and others added 2 commits June 11, 2026 10:07
SprayEngine.execute calls emitStats in a deferred function. When the
caller's context times out, the consumer pipeline may already have shut
down by the time the defer fires, so the OnStats callback sends to a
closed channel and panics.

Check ctx.Err() before invoking statsHandler — if the context is
already cancelled the callback is a no-op.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
TestEmitStats_PanicAfterContextCancel simulates the exact w3 crash:
context cancelled → consumer closes channel → emitStats defer fires →
statsHandler sends on closed channel → panic.

Without the ctx.Err() guard this test fails (panic is triggered).
With the guard it passes (callback is skipped).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant