In arcp-client/src/main/java/dev/arcp/client/ArcpClient.java the listJobs method around line 155 catches ExecutionException | InterruptedException | TimeoutException and rethrows them as a bare new RuntimeException("list_jobs failed", e) on line 165. This is the textbook anti-pattern for exception handling: callers cannot pattern-match the wrapped cause without instanceof gymnastics, the original stack trace is buried, and an InterruptedException is silently consumed without restoring the interrupt flag on the current thread. The result: a caller that wants to distinguish "server timed out" from "thread was interrupted" from "server returned an ArcpException" cannot, and downstream code that polls Thread.interrupted() will not see the interrupt that listJobs swallowed.\n\nThe same anti-pattern shows up in connect at line 133: an ExecutionException is rethrown as an IllegalStateException with no preservation of whether the underlying failure was an ArcpException, an IOException, or a transport error.\n\nFix prompt: In arcp-client/src/main/java/dev/arcp/client/ArcpClient.java rewrite listJobs to declare and throw the relevant typed exceptions. Specifically: re-declare throws InterruptedException, TimeoutException, ArcpException, restore the interrupt flag with Thread.currentThread().interrupt() in the InterruptedException catch then rethrow, propagate TimeoutException directly, and for ExecutionException unwrap the cause — if it is an ArcpException, rethrow it; otherwise wrap in a focused IllegalStateException whose message names the operation. Apply the same pattern to connect(Duration). Update any callers in arcp-client/src/test (RuntimeErrorMappingTest, SmokeRoundTripTest) and the examples directory to handle the new declared exceptions. The change is API-breaking but correct; for a 1.0.0-SNAPSHOT this is the moment to do it.
In arcp-client/src/main/java/dev/arcp/client/ArcpClient.java the listJobs method around line 155 catches
ExecutionException | InterruptedException | TimeoutExceptionand rethrows them as a barenew RuntimeException("list_jobs failed", e)on line 165. This is the textbook anti-pattern for exception handling: callers cannot pattern-match the wrapped cause without instanceof gymnastics, the original stack trace is buried, and an InterruptedException is silently consumed without restoring the interrupt flag on the current thread. The result: a caller that wants to distinguish "server timed out" from "thread was interrupted" from "server returned an ArcpException" cannot, and downstream code that polls Thread.interrupted() will not see the interrupt that listJobs swallowed.\n\nThe same anti-pattern shows up in connect at line 133: an ExecutionException is rethrown as an IllegalStateException with no preservation of whether the underlying failure was an ArcpException, an IOException, or a transport error.\n\nFix prompt: In arcp-client/src/main/java/dev/arcp/client/ArcpClient.java rewrite listJobs to declare and throw the relevant typed exceptions. Specifically: re-declarethrows InterruptedException, TimeoutException, ArcpException, restore the interrupt flag withThread.currentThread().interrupt()in the InterruptedException catch then rethrow, propagate TimeoutException directly, and for ExecutionException unwrap the cause — if it is an ArcpException, rethrow it; otherwise wrap in a focused IllegalStateException whose message names the operation. Apply the same pattern to connect(Duration). Update any callers in arcp-client/src/test (RuntimeErrorMappingTest, SmokeRoundTripTest) and the examples directory to handle the new declared exceptions. The change is API-breaking but correct; for a 1.0.0-SNAPSHOT this is the moment to do it.