{"record":{"id":"7b5b6c64a1be205a","repo":"apache/pulsar","slug":"invalid-time-unit-lastchar-7b5b6c","errorCode":null,"errorMessage":"Invalid time unit '<lastChar>'","messagePattern":"Invalid time unit '<lastChar>'","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-common/src/main/java/org/apache/pulsar/common/util/RelativeTimeUtil.java","lineNumber":65,"sourceCode":"\n        long duration = Long.parseLong(relativeTime.substring(0, lastIndex));\n\n        switch (timeUnit) {\n        case 's':\n            return duration;\n        case 'm':\n            return TimeUnit.MINUTES.toSeconds(duration);\n        case 'h':\n            return TimeUnit.HOURS.toSeconds(duration);\n        case 'd':\n            return TimeUnit.DAYS.toSeconds(duration);\n        case 'w':\n            return 7 * TimeUnit.DAYS.toSeconds(duration);\n        // No unit for months\n        case 'y':\n            return 365 * TimeUnit.DAYS.toSeconds(duration);\n        default:\n            throw new IllegalArgumentException(\"Invalid time unit '\" + lastChar + \"'\");\n        }\n    }\n\n    /**\n     * Convert nanoseconds to seconds and keep three decimal places.\n     * @param ns\n     * @return seconds\n     */\n    public static double nsToSeconds(long ns) {\n        double seconds = (double) ns / 1_000_000_000;\n        BigDecimal bd = new BigDecimal(seconds);\n        return bd.setScale(3, RoundingMode.HALF_UP).doubleValue();\n    }\n}\n","sourceCodeStart":47,"sourceCodeEnd":80,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-common/src/main/java/org/apache/pulsar/common/util/RelativeTimeUtil.java#L47-L80","documentation":"RelativeTimeUtil.parseRelativeTimeInSeconds parses strings like '60s', '5m', '2h', '7d', '2w', '1y' into seconds. The last character is taken as the time unit; if it is alphabetic but not one of s/m/h/d/w/y (case-insensitive), the parser throws this IllegalArgumentException. Note that months are intentionally unsupported, so 'M' and other letters like 'x' or 'z' are rejected.","triggerScenarios":"Calling parseRelativeTimeInSeconds with a string ending in a letter other than s, m, h, d, w, or y (e.g. '30M' for minutes uppercase-collision concerns or months, '1mo', '10x', '5min'). Any user/config supplied relative time value (e.g. TTL, retention, expiry settings) with an unrecognized unit suffix.","commonSituations":"Users write '30M' or '1mo' expecting months or minutes; config files carry units like 'ms', 'min', or 'sec' carried over from other tools; typo in unit suffix ('2hh'); locale or copy-paste issues introduce odd trailing characters.","solutions":["Fix the input string to use only supported units: s, m, h, d, w, or y (e.g. '30m' instead of '30M').","Express months as days or weeks (e.g. '30d') since months are not a supported unit.","Strip surrounding whitespace/typos so the string ends with a valid single-letter unit.","Validate the unit character before calling the parser in user-facing code."],"exampleFix":"// before\nlong seconds = RelativeTimeUtil.parseRelativeTimeInSeconds(\"30M\");\n// after\nlong seconds = RelativeTimeUtil.parseRelativeTimeInSeconds(\"30d\");","handlingStrategy":"validation","validationCode":"private static final java.util.Set<Character> VALID_UNITS = java.util.Set.of('s','m','h','d','w','y');\nstatic long safeParseRelativeTime(String input) {\n    String trimmed = input.trim();\n    if (trimmed.isEmpty()) throw new IllegalArgumentException(\"expiry time cannot be empty\");\n    char last = Character.toLowerCase(trimmed.charAt(trimmed.length() - 1));\n    if (Character.isAlphabetic(last) && !VALID_UNITS.contains(last)) {\n        throw new IllegalArgumentException(\"Invalid time unit '\" + last + \"'; use s, m, h, d, w, or y\");\n    }\n    return RelativeTimeUtil.parseRelativeTimeInSeconds(trimmed);\n}","typeGuard":"static boolean isValidRelativeTimeString(String s) {\n    if (s == null || s.isEmpty()) return false;\n    char last = Character.toLowerCase(s.charAt(s.length() - 1));\n    if (!Character.isAlphabetic(last)) return true; // no unit -> seconds\n    return \"smdwy\".indexOf(last) >= 0;\n}","tryCatchPattern":"try {\n    long seconds = RelativeTimeUtil.parseRelativeTimeInSeconds(userInput);\n} catch (IllegalArgumentException e) {\n    LOG.error(\"Invalid relative time '{}' (units: s, m, h, d, w, y)\", userInput, e);\n    throw new ConfigurationException(\"Invalid time value: \" + userInput, e);\n}","preventionTips":["Only use supported unit suffixes s, m, h, d, w, y in configs and user input","Remember months are not supported — express months as days (e.g. '30d')","Trim and normalize user-provided time strings before parsing","Validate the trailing unit character in config-loading code before calling the parser"],"tags":["pulsar","time-parsing","illegal-argument","configuration"],"backgroundTag":"invalid-time-unit","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}