Swallowed Interrupt Signal

ID

java.interrupt_state_swallowed

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

Java

Tags

reliability

Description

Reports a catch clause for InterruptedException or ThreadDeath — including as one arm of a multi-catch — whose body neither rethrows nor calls Thread.currentThread().interrupt().

Logging the exception is not enough on its own: a body that only logs is reported the same way as an empty one. Any throw statement in the body satisfies the rule, whether it rethrows the caught instance or wraps it in a new exception. Only these two exact types are matched by their simple name — a catch of a broader type such as Exception is out of scope.

A call to a helper that never returns counts as a throw. Agrona’s LangUtil.rethrowUnchecked(e), Guava’s Throwables.propagate(e) and JUnit’s fail(…​) all end the catch body just as a throw statement does, so a catch that terminates through one is not swallowing anything. They are recognised by name, from the configurable throwingCalls list.

Configuration

Property Default Description

throwingCalls

LangUtil.rethrowUnchecked, Throwables.propagate, Throwables.throwIfUnchecked, Exceptions.rethrow, fail, rethrowUnchecked, sneakyThrow

Helpers that never return. Matched against the call name alone, or against Receiver.name using the last part of the receiver, so an import-qualified call is recognised without resolving the import.

Rationale

Both exceptions carry information the JVM would otherwise lose the moment the catch block returns normally: that this thread was asked to stop. A handler that only logs and moves on throws that signal away — the thread keeps running as if nothing happened, so a task the caller believed was cancelled keeps executing, and the next check for cancellation never sees that it should have stopped.

try {
  process(queue.take());
} catch (InterruptedException e) {
  LOGGER.warn("Interrupted!", e); // the interrupt is gone once this returns
}

Remediation

Either restore the interrupt flag before returning, or propagate the exception:

try {
  process(queue.take());
} catch (InterruptedException e) {
  LOGGER.warn("Interrupted!", e);
  Thread.currentThread().interrupt(); // restore the signal for the next check
}

© 2026 Xygeni. All rights reserved