fix(mcp): swallow broken/closed-pipe on dg mcp startup notifications and error path#88
fix(mcp): swallow broken/closed-pipe on dg mcp startup notifications and error path#88dg-coreylweathers wants to merge 2 commits into
Conversation
…and error path When `dg mcp` runs as an MCP server and the host closes stdio, the startup update-notification write to stderr raised BrokenPipeError, which then cascaded into an unhandled ValueError from rich while the error handler tried to print to the already-closed console — crashing through the excepthook and generating a Sentry event (DX-CLI-P). - Guard all three startup notification writers (update, plugin, skills) against (BrokenPipeError, OSError, ValueError), matching the existing closed-stream pattern in base_command.py. - Route main()'s error/interrupt handlers through a new _safe_console_print() helper so a closed console no longer crashes the process on exit. - Defense-in-depth: drop broken-pipe / closed-file events in the Sentry before_send hook (_is_broken_pipe) so residual pipe-close on dg mcp produces no noise. Adds unit coverage for the notification guard and the Sentry filter. Sentry: https://deepgram.sentry.io/issues/DX-CLI-P Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds the missing regression tests for the three DX-CLI-P guards that shipped without coverage: - _safe_console_print (main.py): swallows BrokenPipeError / OSError / rich's closed-file ValueError, plus an end-to-end test that main() still exits(2) when the cli raises AND the console is closed (the exact crash cascade). - skills print_pending_notification: tolerates a broken stderr. - plugin print_pending_plugin_notifications: tolerates a broken stderr. Each guard test fails if its try/except is removed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reviewer context — DX-CLI-PWhat broke: when The fix (commit 6091c6a):
Coverage (commit 5b9ea16): added regression tests for all three guards, including an end-to-end test that reproduces the exact cascade — cli raises and the console is closed — and asserts Verification:
One earlier review nit (bare |
Summary
Fixes DX-CLI-P —
dg mcpcrashes withValueError: I/O operation on closed fileon a broken stderr pipe.When
dgruns as an MCP server and the host closes stdio (a normal condition on client disconnect), the crash was a two-stage cascade:print_pending_notification()does a rawsys.stderr.write(...). On a closed pipe this raisesBrokenPipeError.except Exceptionhandler inmain()then calledconsole.print(...), but the console's underlying stream is also closed, so rich raisedValueError: I/O operation on closed file— an unhandled exception that escaped to the excepthook, crashing the process and generating a Sentry event.Changes
(BrokenPipeError, OSError, ValueError), matching the existing closed-stream pattern inbase_command.py.main()'s error path stream-safe via a new_safe_console_print()helper, so a closed console no longer crashes the process on exit. Both theKeyboardInterruptand genericExceptionhandlers route through it.before_sendhook (_is_broken_pipe) so residual pipe-close ondg mcpproduces no noise.Testing
test_telemetry.py+test_startup_check.py: 42 passed (incl. 6 new)ruff check/ruff format --check/mypyon changed files: cleanprint_pending_notification()(survives without raising) and ran a realdg mcpprocess with a broken output pipe (exit 0, no traceback leaked).Sentry: https://deepgram.sentry.io/issues/DX-CLI-P
🤖 Generated with Claude Code