{"record":{"id":"b4532ae88021588c","repo":"pinpoint-apm/pinpoint","slug":"list-size-not-same","errorCode":null,"errorMessage":"list size not same","messagePattern":"list size not same","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"agent-module/profiler/src/main/java/com/navercorp/pinpoint/profiler/context/compress/GrpcSpanProcessorV2.java","lineNumber":88,"sourceCode":"        pSpanChunk.setKeyTime(keyTime);\n        postProcess(keyTime, spanEventList, tSpanEventList);\n    }\n\n    @Override\n    public void postProcess(Span span, PSpan.Builder pSpan) {\n        final List<SpanEvent> spanEventList = span.getSpanEventList();\n        final List<PSpanEvent.Builder> tSpanEventList = pSpan.getSpanEventBuilderList();\n        long keyTime = span.getStartTime();\n        postProcess(keyTime, spanEventList, tSpanEventList);\n    }\n\n    private void postProcess(long keyTime, List<SpanEvent> spanEventList, List<PSpanEvent.Builder> pSpanEventList) {\n        final int spanEventSize = CollectionUtils.nullSafeSize(spanEventList);\n        if (spanEventSize == 0) {\n            return;\n        }\n        if (!(spanEventSize == CollectionUtils.nullSafeSize(pSpanEventList))) {\n            throw new IllegalStateException(\"list size not same\");\n        }\n        // check list type\n        assert spanEventList instanceof RandomAccess;\n\n        int prevDepth = 0;\n        boolean first = true;\n\n        final int listSize = spanEventList.size();\n        for (int i = 0; i < listSize; i++) {\n            final SpanEvent spanEvent = spanEventList.get(i);\n            final PSpanEvent.Builder pSpanEvent = pSpanEventList.get(i);\n\n            final long startTime = spanEvent.getStartTime();\n            final long startElapsedTime = startTime - keyTime;\n            pSpanEvent.setStartElapsed((int) startElapsedTime);\n            keyTime = startTime;\n\n            if (first) {","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/pinpoint-apm/pinpoint/blob/744c3d3075e595656abb1ae331ad2c0e4c9eb996/agent-module/profiler/src/main/java/com/navercorp/pinpoint/profiler/context/compress/GrpcSpanProcessorV2.java#L70-L106","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nList<PSpanEvent.Builder> builders = events.stream()\n    .filter(e -> e.isFiltered())   // only filters one side?\n    .map(this::toBuilder).collect(toList());\nprocessor.spanPostProcess(eventList, builders); // sizes differ -> IllegalStateException\n// after\nList<PSpanEvent.Builder> builders = new ArrayList<>(eventList.size());\nfor (SpanEvent e : eventList) {\n    if (e.isFiltered()) {\n        builders.add(toBuilder(e));\n    }\n}\nList<SpanEvent> kept = filterEvents(eventList); // filter the source list identically\nprocessor.spanPostProcess(kept, builders); // sizes guaranteed equal","handlingStrategy":"try-catch","validationCode":"if (CollectionUtils.nullSafeSize(spanEventList) != CollectionUtils.nullSafeSize(pSpanEventList)) {\n    throw new AssertionError(\"converter dropped events: \"\n        + CollectionUtils.nullSafeSize(spanEventList) + \" vs \"\n        + CollectionUtils.nullSafeSize(pSpanEventList));\n} // run before invoking the processor to fail fast with context","typeGuard":"boolean isAligned(List<?> a, List<?> b) {\n    return CollectionUtils.nullSafeSize(a) == CollectionUtils.nullSafeSize(b);\n}","tryCatchPattern":"try {\n    spanPostProcess(spanEventList, pSpanEventList);\n} catch (IllegalStateException e) {\n    logger.error(\"Span event list misaligned: {} vs {}\",\n        spanEventList.size(), pSpanEventList.size(), e);\n}","preventionTips":["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"],"tags":["java","pinpoint","grpc","span","internal-invariant"],"backgroundTag":"internal-invariant-violation","analyzedSha":"744c3d3075e595656abb1ae331ad2c0e4c9eb996","analyzedAt":"2026-09-07T18:48:45.289Z","contentChangedAt":"2026-09-07T18:48:45.289Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}