{"record":{"id":"0623eef7f2f632ca","repo":"pinpoint-apm/pinpoint","slug":"span-event-end-time-is-not-set","errorCode":null,"errorMessage":"span event end time is not set","messagePattern":"span event end time is not set","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"commons-server/src/main/java/com/navercorp/pinpoint/common/server/bo/SpanEventBo.java","lineNumber":164,"sourceCode":"\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    }\n\n    public boolean hasEndTime() {\n        return Byte.toUnsignedInt(version) == SpanVersion.TRACE_V3 && endTime != DEFAULT_END_TIME;\n    }\n\n    public long getEndTimeNanos() {\n        if (!hasEndTime()) {\n            throw new IllegalStateException(\"span event end time is not set\");\n        }\n        return endTime;\n    }\n\n    public int getServiceType() {\n        return serviceType;\n    }\n\n    public void setServiceType(int serviceType) {\n        this.serviceType = serviceType;\n    }\n\n    public String getEndPoint() {\n        return endPoint;\n    }\n\n    public void setEndPoint(String endPoint) {\n        this.endPoint = endPoint;","sourceCodeStart":146,"sourceCodeEnd":182,"githubUrl":"https://github.com/pinpoint-apm/pinpoint/blob/744c3d3075e595656abb1ae331ad2c0e4c9eb996/commons-server/src/main/java/com/navercorp/pinpoint/common/server/bo/SpanEventBo.java#L146-L182","documentation":"getEndTimeNanos returns the absolute end time in nanos, available only for TRACE_V3 span events whose endTime was explicitly set (not the DEFAULT_END_TIME sentinel). Otherwise the value is undefined and the method throws IllegalStateException.","triggerScenarios":"Calling getEndTimeNanos on a non-TRACE_V3 span event, or a V3 event created with the relative-elapsed overload where endTime remained DEFAULT_END_TIME.","commonSituations":"UI/collector code computing event durations reading V2 events; callers not checking hasEndTime() first; V3 events decoded through the wrong (relative-time) branch.","solutions":["Call hasEndTime() before getEndTimeNanos() and compute the end from parent start + elapsed when absent","Build TRACE_V3 events exclusively with the absolute-time setTraceTime overload","Verify the decode path assigns V3 events both start and end absolute times"],"exampleFix":"// before\nlong end = spanEventBo.getEndTimeNanos();\n// after\nlong end = spanEventBo.hasEndTime()\n    ? spanEventBo.getEndTimeNanos()\n    : parentStartTime + TimeUnit.MILLISECONDS.toNanos(spanEventBo.getStartElapsed() + spanEventBo.getEndElapsed());","handlingStrategy":"type-guard","validationCode":"long end = spanEventBo.hasEndTime()\n    ? spanEventBo.getEndTimeNanos()\n    : fallbackEndTime(spanEventBo);","typeGuard":"boolean canReadEndTime(SpanEventBo e) {\n    return e.hasEndTime();\n}","tryCatchPattern":"try {\n    return spanEventBo.getEndTimeNanos();\n} catch (IllegalStateException e) {\n    return parentStartTime + TimeUnit.MILLISECONDS.toNanos(spanEventBo.getStartElapsed() + spanEventBo.getEndElapsed());\n}","preventionTips":["Guard with hasEndTime() before calling getEndTimeNanos()","Ensure V3 decode paths set both start and end absolute times","Treat V2 events with elapsed-based logic, never absolute-nano getters"],"tags":["java","illegal-state","trace-versioning"],"backgroundTag":"invalid-state-transition","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"}