fix(agent): prevent panic from closing events channel while readLoop may still send
In 6 agent session implementations (codex, opencode, qoder, iflow, cursor, gemini), Close() unconditionally calls close(events) after an 8-second timeout, even when the readLoop goroutine may still be running and sending events to the channel. Due to Go's select non-determinism, the readLoop goroutine can choose the send case over the ctx.Done() case, causing a "send on closed channel" panic. Move close(events) into the case where wg.Wait() succeeds (readLoop has fully exited), and skip closing on timeout to prevent the race. This follows the same safe pattern already used by claudeSession, where the events channel is only closed after the readLoop goroutine has finished.
This commit is contained in:
1 parent
6e87769231
commit
be74fddca2
6 files changed
+6
-6
No files matched your search
@@ -497,10 +497,10 @@ func (cs *codexSession) Close() error {
|
||||
}()
|
||||
select {
|
||||
case <-done:
|
||||
close(cs.events)
|
||||
case <-time.After(8 * time.Second):
|
||||
slog.Warn("codexSession: close timed out, abandoning wg.Wait")
|
||||
}
|
||||
close(cs.events)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -464,10 +464,10 @@ func (cs *cursorSession) Close() error {
|
||||
}()
|
||||
select {
|
||||
case <-done:
|
||||
close(cs.events)
|
||||
case <-time.After(8 * time.Second):
|
||||
slog.Warn("cursorSession: close timed out, abandoning wg.Wait")
|
||||
}
|
||||
close(cs.events)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -469,10 +469,10 @@ func (gs *geminiSession) Close() error {
|
||||
}()
|
||||
select {
|
||||
case <-done:
|
||||
close(gs.events)
|
||||
case <-time.After(8 * time.Second):
|
||||
slog.Warn("geminiSession: close timed out, abandoning wg.Wait")
|
||||
}
|
||||
close(gs.events)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -897,9 +897,9 @@ func (s *iflowSession) Close() error {
|
||||
}()
|
||||
select {
|
||||
case <-done:
|
||||
close(s.events)
|
||||
case <-time.After(8 * time.Second):
|
||||
slog.Warn("iflowSession: close timed out, abandoning wg.Wait")
|
||||
}
|
||||
close(s.events)
|
||||
return nil
|
||||
}
|
||||
@@ -393,10 +393,10 @@ func (s *opencodeSession) Close() error {
|
||||
}()
|
||||
select {
|
||||
case <-done:
|
||||
close(s.events)
|
||||
case <-time.After(8 * time.Second):
|
||||
slog.Warn("opencodeSession: close timed out, abandoning wg.Wait")
|
||||
}
|
||||
close(s.events)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -288,10 +288,10 @@ func (qs *qoderSession) Close() error {
|
||||
}()
|
||||
select {
|
||||
case <-done:
|
||||
close(qs.events)
|
||||
case <-time.After(8 * time.Second):
|
||||
slog.Warn("qoderSession: close timed out, abandoning wg.Wait")
|
||||
}
|
||||
close(qs.events)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user