pinpoint-apm/pinpoint · error · IllegalStateException
list size not same
Error message
list size not same
What it means
GrpcSpanProcessorV2 converts a trace's SpanEvent list into protobuf PSpanEvent.Builder list, keeping both lists index-aligned so depth/sequence can be written in a single post-processing pass. postProcess() enforces the invariant that the input and output lists have the same size and throws IllegalStateException("list size not same") when they diverge, because alignment-based compression would otherwise corrupt span data. This is a hard internal consistency check, not user input validation.
Source
Thrown at agent-module/profiler/src/main/java/com/navercorp/pinpoint/profiler/context/compress/GrpcSpanProcessorV2.java:88
pSpanChunk.setKeyTime(keyTime);
postProcess(keyTime, spanEventList, tSpanEventList);
}
@Override
public void postProcess(Span span, PSpan.Builder pSpan) {
final List<SpanEvent> spanEventList = span.getSpanEventList();
final List<PSpanEvent.Builder> tSpanEventList = pSpan.getSpanEventBuilderList();
long keyTime = span.getStartTime();
postProcess(keyTime, spanEventList, tSpanEventList);
}
private void postProcess(long keyTime, List<SpanEvent> spanEventList, List<PSpanEvent.Builder> pSpanEventList) {
final int spanEventSize = CollectionUtils.nullSafeSize(spanEventList);
if (spanEventSize == 0) {
return;
}
if (!(spanEventSize == CollectionUtils.nullSafeSize(pSpanEventList))) {
throw new IllegalStateException("list size not same");
}
// check list type
assert spanEventList instanceof RandomAccess;
int prevDepth = 0;
boolean first = true;
final int listSize = spanEventList.size();
for (int i = 0; i < listSize; i++) {
final SpanEvent spanEvent = spanEventList.get(i);
final PSpanEvent.Builder pSpanEvent = pSpanEventList.get(i);
final long startTime = spanEvent.getStartTime();
final long startElapsedTime = startTime - keyTime;
pSpanEvent.setStartElapsed((int) startElapsedTime);
keyTime = startTime;
if (first) {View on GitHub (pinned to 744c3d3075)
Solutions
- Audit the code that builds pSpanEventList from spanEventList to ensure every retained SpanEvent yields exactly one builder (filter both lists together).
- Fix or remove custom SpanEventFilter implementations that exclude events after the builder list was created.
- Ensure spanPostProcess is called with the same list instances that were used during spanEvent building, not copies.
- Reproduce with a large/recursive span chunk and log both sizes to identify which events are dropped.
- Upgrade the Pinpoint agent if the mismatch is caused by a known bug in the V2 processor.
Example fix
// before
List<PSpanEvent.Builder> builders = events.stream()
.filter(e -> e.isFiltered()) // only filters one side?
.map(this::toBuilder).collect(toList());
processor.spanPostProcess(eventList, builders); // sizes differ -> IllegalStateException
// after
List<PSpanEvent.Builder> builders = new ArrayList<>(eventList.size());
for (SpanEvent e : eventList) {
if (e.isFiltered()) {
builders.add(toBuilder(e));
}
}
List<SpanEvent> kept = filterEvents(eventList); // filter the source list identically
processor.spanPostProcess(kept, builders); // sizes guaranteed equal Defensive patterns
Strategy: try-catch
Validate before calling
if (CollectionUtils.nullSafeSize(spanEventList) != CollectionUtils.nullSafeSize(pSpanEventList)) {
throw new AssertionError("converter dropped events: "
+ CollectionUtils.nullSafeSize(spanEventList) + " vs "
+ CollectionUtils.nullSafeSize(pSpanEventList));
} // run before invoking the processor to fail fast with context Type guard
boolean isAligned(List<?> a, List<?> b) {
return CollectionUtils.nullSafeSize(a) == CollectionUtils.nullSafeSize(b);
} Try / catch
try {
spanPostProcess(spanEventList, pSpanEventList);
} catch (IllegalStateException e) {
logger.error("Span event list misaligned: {} vs {}",
spanEventList.size(), pSpanEventList.size(), e);
} Prevention
- Filter the source SpanEvent list and builder list together, never independently
- Unit-test span post-processing with nested/recursive events
- Avoid mutating the span event list after builders are created
- Keep custom SpanEventFilter logic consistent with builder creation
- Pin agent component versions so processor V2 invariants hold
When it happens
Trigger: Calling postProcess (via spanPostProcess) when the filtered spanEventList and the built pSpanEventList have different lengths — e.g. some SpanEvents were skipped during filtering/builder creation while the source list still contains them, or events were appended to only one of the two lists.
Common situations: Bugs in custom SpanEvent filters or span event converters that drop events on one side; modifications to the span-event list between buildSpanChunk/buildSpan and postProcess; agent version mismatches where processor V2 invariants are violated by newer/older event structures; corrupted trace state from earlier lifecycle errors.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- spanEventList is empty.
- first SpanEvent is null
- unsupported message
- Failed to request. header={}
- PTransactionId is not set
AI-assisted analysis of pinpoint-apm/pinpoint@744c3d3075 (2026-09-07).
Data as JSON: /api/errors/b4532ae88021588c.
Report an issue: GitHub.