{"record":{"id":"3d3db239a422de7b","repo":"pinpoint-apm/pinpoint","slug":"span-event-end-time-must-be-greater-than-or-equal","errorCode":null,"errorMessage":"span event end time must be greater than or equal to start time","messagePattern":"span event end time must be greater than or equal to start time","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"commons-server/src/main/java/com/navercorp/pinpoint/common/server/bo/SpanEventBo.java","lineNumber":138,"sourceCode":"        this.startTime = DEFAULT_START_TIME;\n        this.endTime = DEFAULT_END_TIME;\n        this.startElapsed = startElapsedMillis;\n        this.endElapsed = endElapsedMillis;\n    }\n\n    /**\n     * Sets absolute span event time for TRACE_V3 data.\n     * startTime/endTime are epoch nanos. startElapsedMillis is retained as the compatibility\n     * offset from the parent span/chunk start; endElapsedMillis is derived from endTime-startTime.\n     */\n    public void setTraceTime(int version, long startTime, long endTime, int startElapsedMillis) {\n        setVersion((byte) version);\n\n        if (version != SpanVersion.TRACE_V3) {\n            throw new IllegalArgumentException(\"absolute start/end time is only supported for TRACE_V3 span events\");\n        }\n        if (endTime < startTime) {\n            throw new IllegalArgumentException(\"span event end time must be greater than or equal to start time\");\n        }\n\n        this.startTime = startTime;\n        this.endTime = endTime;\n        this.startElapsed = startElapsedMillis;\n        this.endElapsed = (int) TimeUnit.NANOSECONDS.toMillis(endTime - startTime);\n    }\n\n    public boolean hasStartTime() {\n        return Byte.toUnsignedInt(version) == SpanVersion.TRACE_V3 && startTime != DEFAULT_START_TIME;\n    }\n\n    public long getStartTimeNanos() {\n        if (!hasStartTime()) {\n            throw new IllegalStateException(\"span event start time is not set\");\n        }\n        return startTime;\n    }","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/pinpoint-apm/pinpoint/blob/744c3d3075e595656abb1ae331ad2c0e4c9eb996/commons-server/src/main/java/com/navercorp/pinpoint/common/server/bo/SpanEventBo.java#L120-L156","documentation":"When constructing a TRACE_V3 span event with absolute times, the library validates that endTime >= startTime before deriving endElapsed = endTime - startTime. A negative duration is semantically invalid for a span event, so it is rejected with this IllegalArgumentException.","triggerScenarios":"Calling setTraceTime(version, startTime, endTime, startElapsedMillis) with endTime < startTime, e.g. from readSpanEvent/bind on rows whose encoded end nanos precede the start nanos, or from code computing timestamps out of order.","commonSituations":"Clock skew or timestamp source mixing (e.g. different System.nanoTime origins) when building V3 events; corrupted serialized nanos; buggy instrumentation that records the end timestamp before the start.","solutions":["Ensure the end timestamp is captured at or after the start timestamp in the instrumentation","Validate startTime <= endTime before calling setTraceTime and clamp or log invalid durations","Check for mixing of different nanoTime/epoch bases when producing the two timestamps"],"exampleFix":"// before\nspanEventBo.setTraceTime(version, startNanos, endNanos, elapsed); // endNanos may be < startNanos\n// after\nlong end = Math.max(endNanos, startNanos);\nspanEventBo.setTraceTime(version, startNanos, end, (int) TimeUnit.NANOSECONDS.toMillis(end - startNanos));","handlingStrategy":"validation","validationCode":"if (endNanos < startNanos) {\n    endNanos = startNanos; // clamp zero-duration\n}\nspanEventBo.setTraceTime(version, startNanos, endNanos, elapsed);","typeGuard":"boolean isValidDuration(long startNanos, long endNanos) {\n    return endNanos >= startNanos;\n}","tryCatchPattern":"try {\n    spanEventBo.setTraceTime(version, startNanos, endNanos, elapsed);\n} catch (IllegalArgumentException e) {\n    logger.warn(\"Negative span duration start=\" + startNanos + \" end=\" + endNanos, e);\n}","preventionTips":["Capture the end timestamp strictly after the start timestamp","Never mix timestamp sources (nanoTime vs epoch, different origins)","Clamp or log non-positive durations at the instrumentation layer"],"tags":["java","timestamps","validation"],"backgroundTag":"invalid-argument-value","analyzedSha":"744c3d3075e595656abb1ae331ad2c0e4c9eb996","analyzedAt":"2026-09-07T18:48:45.289Z","contentChangedAt":"2026-09-07T18:48:45.289Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}