chenhg5/cc-connect · error
piSession: write extension_ui_response: %w
Error message
piSession: write extension_ui_response: %w
What it means
RespondPermission in agent/pi/session.go fails when writing the marshalled extension_ui_response line to the pi RPC child's stdin fails. Like error 320, this means the pipe to the pi process is closed or broken — typically because the child exited or stdin was already closed. The write is serialized through rpcStdinMu.
Source
Thrown at agent/pi/session.go:1201
resp = map[string]any{
"type": "extension_ui_response",
"id": extID,
"confirmed": result.Behavior == "allow",
}
}
b, err := json.Marshal(resp)
if err != nil {
return fmt.Errorf("piSession: marshal extension_ui_response: %w", err)
}
b = append(b, '\n')
slog.Debug("piSession: sending extension_ui_response", "id", extID, "behavior", result.Behavior)
s.rpcStdinMu.Lock()
_, err = s.rpcStdin.Write(b)
s.rpcStdinMu.Unlock()
if err != nil {
return fmt.Errorf("piSession: write extension_ui_response: %w", err)
}
return nil
}
// ── AgentSession interface ──────────────────────────────────
func (s *piSession) Events() <-chan core.Event {
return s.events
}
func (s *piSession) CurrentSessionID() string {
v, _ := s.sessionID.Load().(string)
return v
}
func (s *piSession) Alive() bool {
return s.alive.Load()View on GitHub (pinned to 4000b2338a)
Solutions
- Check the wrapped cause (os.ErrClosed / EPIPE) to confirm the child is gone
- Recreate the session and re-issue the request — the pending permission is unrecoverable
- Ensure Close()/Stop() resolves pending permissions before closing stdin to avoid races
- Monitor pi process health (exit status) to detect crashes early
Example fix
// before
if err := sess.RespondPermission(ctx, id, result); err != nil {
return err
}
// after
if err := sess.RespondPermission(ctx, id, result); err != nil {
if errors.Is(err, os.ErrClosed) {
slog.Warn("pi session gone, dropping permission response", "id", id)
return nil
}
return err
} Defensive patterns
Strategy: try-catch
Validate before calling
if !sess.Alive() {
return errors.New("cannot respond: pi session closed")
} Type guard
func isPipeClosed(err error) bool {
return errors.Is(err, os.ErrClosed) || errors.Is(err, io.ErrClosedPipe) || errors.Is(err, syscall.EPIPE)
} Try / catch
if err := sess.RespondPermission(ctx, id, result); err != nil {
if isPipeClosed(err) {
slog.Warn("pi gone, permission response dropped", "id", id)
return nil // or recreate session and re-request
}
return err
} Prevention
- Answer permission prompts promptly; the process may be torn down otherwise
- Make Close/Stop resolve pending permissions before closing stdin
- Detect child exit and invalidate outstanding permission promises
- Retry at most once with a fresh session
When it happens
Trigger: Answering a permission request after the pi process has died or is shutting down; Close() racing with a pending permission response; child crash mid-permission-prompt.
Common situations: User takes too long to approve and the pi process was stopped in between; pi crashed while a tool permission prompt was pending; daemon shutdown closing sessions while responses are in flight.
Related errors
- piSession: write stdin: %w
- read existing Agy hooks %s: %w
- write hook response: %w
- write stdin: %w
- codex app-server initialized notify: %w
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/d099400dece8b7bf.
Report an issue: GitHub.