diff --git a/CHANGELOG.md b/CHANGELOG.md index ef96825a..573abbfd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +### Fixed + +- The CLI no longer sends an error report when a write fails with an input/output error (EIO). For example, this occurs when the terminal closes or the SSH session drops while a command runs. The CLI handles the error in the same way as before. + ## [1.31.0] - 2026-11-11 ### Added diff --git a/cloudsmith_cli/core/telemetry.py b/cloudsmith_cli/core/telemetry.py index 50adac79..35f3a942 100644 --- a/cloudsmith_cli/core/telemetry.py +++ b/cloudsmith_cli/core/telemetry.py @@ -22,6 +22,7 @@ ``sentry_sdk`` must only ever be imported inside :func:`_send`. """ +import errno import os import platform import re @@ -149,10 +150,12 @@ def is_reportable(exc: BaseException) -> bool: ``report_to_telemetry = False``. Checked by attribute rather than by importing the types, so this module stays light on the error path. A failed TLS certificate check is a problem with the network, not with the CLI. + An EIO error comes from the device, such as a terminal that hung up. """ return ( getattr(exc, REPORTABLE_ATTR, True) is not False and find_certificate_error(exc) is None + and not (isinstance(exc, OSError) and exc.errno == errno.EIO) ) diff --git a/cloudsmith_cli/core/tests/test_telemetry.py b/cloudsmith_cli/core/tests/test_telemetry.py index 757ae1b8..98c39940 100644 --- a/cloudsmith_cli/core/tests/test_telemetry.py +++ b/cloudsmith_cli/core/tests/test_telemetry.py @@ -1,5 +1,6 @@ """Tests for error reporting: opt-out controls, sending, and anonymisation.""" +import errno import importlib.util import json import socket @@ -154,6 +155,14 @@ class SpecificUserError(UserError): assert sentry_events == [] +def test_device_io_errors_are_not_reported(sentry_events): + """A terminal that hangs up mid-command fails the next write with EIO.""" + exc = raise_and_catch(OSError(errno.EIO, "Input/output error")) + + assert telemetry.report_exception(exc) is False + assert sentry_events == [] + + def test_exceptions_are_reportable_by_default(): assert telemetry.is_reportable(RuntimeError("boom")) is True