Skip to content
This repository was archived by the owner on Jan 23, 2026. It is now read-only.

Commit b000ec6

Browse files
mangelajogithub-actions[bot]
authored andcommitted
Handle CTRL+C CancelledError exception
(cherry picked from commit 10dcc82)
1 parent f416d43 commit b000ec6

2 files changed

Lines changed: 13 additions & 9 deletions

File tree

packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import sys
66
import threading
77
import time
8+
from concurrent.futures import CancelledError
89
from contextlib import contextmanager
910
from dataclasses import dataclass
1011
from pathlib import Path, PosixPath
@@ -233,6 +234,10 @@ def _categorize_exception(self, exception: Exception) -> FlashRetryableError | F
233234
if retryable is not None:
234235
return retryable
235236

237+
# CancelledError is a special case that should be treated as non-retryable
238+
if isinstance(exception, CancelledError):
239+
return FlashNonRetryableError("Operation cancelled")
240+
236241
# Unknown exception - log full stack trace and wrap as retryable
237242
self.logger.exception(
238243
f"Unknown exception encountered during flash operation, treating as retryable: "

packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client_test.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from concurrent.futures import CancelledError
2+
13
import click
24
import pytest
35

@@ -94,18 +96,15 @@ def test_categorize_exception_wraps_unknown_exceptions():
9496
assert result.__cause__ is error
9597

9698

97-
def test_categorize_exception_non_retryable_takes_priority_over_retryable():
98-
"""Test that non-retryable errors take priority in cause chain"""
99+
def test_categorize_exception_cancelled_error_is_non_retryable():
100+
"""Test that CancelledError is treated as non-retryable"""
99101
client = MockFlasherClient()
100102

101-
# Create a chain: retryable caused by non-retryable
102-
non_retryable = FlashNonRetryableError("Config issue")
103-
retryable = FlashRetryableError("Network error")
104-
retryable.__cause__ = non_retryable
105-
106-
result = client._categorize_exception(retryable)
103+
# CancelledError should be treated as non-retryable
104+
error = CancelledError()
105+
result = client._categorize_exception(error)
107106
assert isinstance(result, FlashNonRetryableError)
108-
assert str(result) == "Config issue"
107+
assert "Operation cancelled" in str(result)
109108

110109

111110
def test_categorize_exception_searches_cause_chain():

0 commit comments

Comments
 (0)