{"record":{"id":"0627fe147b153bae","repo":"apache/skywalking","slug":"end-time-must-not-be-before-start","errorCode":null,"errorMessage":"End time must not be before start","messagePattern":"End time must not be before start","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/DurationUtils.java","lineNumber":229,"sourceCode":"                YYYY_MM_DD.parseDateTime(dateStr);\n                return;\n            case HOUR:\n                YYYY_MM_DD_HH.parseDateTime(dateStr);\n                return;\n            case MINUTE:\n                YYYY_MM_DD_HHMM.parseDateTime(dateStr);\n                return;\n            case SECOND:\n                YYYY_MM_DD_HHMMSS.parseDateTime(dateStr);\n                return;\n        }\n        throw new UnexpectedException(\"Unsupported step \" + step.name());\n    }\n\n    public static Duration timestamp2Duration(long startTS, long endTS) {\n        Duration duration = new Duration();\n        if (endTS < startTS) {\n            throw new IllegalArgumentException(\"End time must not be before start\");\n        }\n        DateTime startDT = new DateTime(startTS);\n        DateTime endDT = new DateTime(endTS);\n\n        long durationValue = endTS - startTS;\n\n        if (durationValue <= 3600000) {\n            duration.setStep(Step.MINUTE);\n            duration.setStart(startDT.toString(DurationUtils.YYYY_MM_DD_HHMM));\n            duration.setEnd(endDT.toString(DurationUtils.YYYY_MM_DD_HHMM));\n        } else if (durationValue <= 86400000) {\n            duration.setStep(Step.HOUR);\n            duration.setStart(startDT.toString(DurationUtils.YYYY_MM_DD_HH));\n            duration.setEnd(endDT.toString(DurationUtils.YYYY_MM_DD_HH));\n        } else {\n            duration.setStep(Step.DAY);\n            duration.setStart(startDT.toString(DurationUtils.YYYY_MM_DD));\n            duration.setEnd(endDT.toString(DurationUtils.YYYY_MM_DD));","sourceCodeStart":211,"sourceCodeEnd":247,"githubUrl":"https://github.com/apache/skywalking/blob/102af09b4a56064e22050dded10e2c52e490d040/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/DurationUtils.java#L211-L247","documentation":"DurationUtils.timestamp2Duration() converts a pair of epoch-millisecond timestamps into a Duration object (auto-selecting MINUTE/HOUR/DAY step by span). It rejects inverted input — endTS < startTS — with IllegalArgumentException, since a negative span cannot be represented. This guards the query API against nonsensical time windows before they hit storage.","triggerScenarios":"Calling timestamp2Duration(startTS, endTS) (directly or via a query path that builds Duration from timestamps) where the end timestamp is numerically smaller than the start.","commonSituations":"Client code passing Date.now() as end and a constant as start in the wrong argument order; unit-test fixtures with hand-written inverted timestamps; timezone/millisecond-vs-second confusion making 'end' parse as an earlier instant; UI sending an empty end that defaults to 0.","solutions":["Swap or correct the argument order so endTS >= startTS at the call site.","Validate/normalize both timestamps (epoch millis, not seconds) before calling; multiply second-epoch values by 1000L.","Default a missing end to now() rather than 0."],"exampleFix":"// before\nDuration d = DurationUtils.timestamp2Duration(endTS, startTS);\n\n// after\nDuration d = DurationUtils.timestamp2Duration(startTS, endTS);","handlingStrategy":"validation","validationCode":"if (endTS < startTS) throw new IllegalArgumentException(\"end must be >= start\");\nDuration d = DurationUtils.timestamp2Duration(startTS, endTS);","typeGuard":"boolean isValidWindow(long startTS, long endTS) { return endTS >= startTS && startTS > 0 && endTS <= System.currentTimeMillis() + 86_400_000L; }","tryCatchPattern":"try { Duration d = DurationUtils.timestamp2Duration(startTS, endTS); } catch (IllegalArgumentException e) { if (\"End time must not be before start\".equals(e.getMessage())) { long t = startTS; startTS = endTS; endTS = t; /* or surface a form error */ } else throw e; }","preventionTips":["Name variables explicitly (startTS/endTS) — argument-order swaps are the top cause.","Assert epoch units (millis) at API boundaries; reject second-epoch values.","Default missing end to now(), never 0."],"tags":["query","duration","validation","timestamps"],"backgroundTag":null,"analyzedSha":"102af09b4a56064e22050dded10e2c52e490d040","analyzedAt":"2026-08-14T10:47:52.647Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}