Reused Consumed Stream

ID

java.consumed_stream_pipelines_reused

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

Java

Tags

reliability

Description

Reports a Stream-typed local variable that is used again, later in the same method, after a terminal operation (forEach, collect, count, …​) has already run through it.

The two usages must be able to run on one and the same path. A consumption that sits under a branch the later usage is outside of — the two arms of an if or a switch, a returning if and the code that follows it, a loop or lambda body — is not reported: one of the two never runs, so nothing is traversed twice. The test is one-sided, so a usage that always runs followed by one inside a branch is still reported; it throws whenever that branch is taken.

A consumption inside a loop body or an if condition followed by a later use is a known false negative of that test.

Rationale

A JDK Stream can be traversed only once. Once a terminal operation has consumed it, any further operation on the same reference — terminal or not — throws IllegalStateException at runtime:

Stream<String> pipeline = names.stream();
pipeline.forEach(System.out::println);
long total = pipeline.count(); // throws IllegalStateException: stream has already been operated upon or closed

Remediation

Build the pipeline again from the source collection for the second traversal, or restructure the code so a single pipeline produces everything that is needed:

Stream<String> pipeline = names.stream();
pipeline.forEach(System.out::println);
long total = names.stream().count();

What is not reported

Only a local declared with the exact type Stream is tracked — a var-declared local, and the primitive stream types (IntStream, LongStream, DoubleStream), are out of scope. Usages are only followed within the declaring method: a field or a variable captured by a lambda and used elsewhere is not tracked. The check has no control-flow awareness — a reassignment (pipeline = …​;) is treated as a fresh value and clears the "already consumed" state, but two usages in mutually exclusive if/else branches are still seen as sequential.

© 2026 Xygeni. All rights reserved