{"record":{"id":"9272df6d5bcb3e33","repo":"apache/pulsar","slug":"expiry-time-cannot-be-empty","errorCode":null,"errorMessage":"expiry time cannot be empty","messagePattern":"expiry time cannot be empty","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-common/src/main/java/org/apache/pulsar/common/util/RelativeTimeUtil.java","lineNumber":33,"sourceCode":" * KIND, either express or implied.  See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\npackage org.apache.pulsar.common.util;\n\nimport java.math.BigDecimal;\nimport java.math.RoundingMode;\nimport java.util.concurrent.TimeUnit;\nimport lombok.experimental.UtilityClass;\n\n/**\n * Parser for relative time.\n */\n@UtilityClass\npublic class RelativeTimeUtil {\n    public static long parseRelativeTimeInSeconds(String relativeTime) {\n        if (relativeTime.isEmpty()) {\n            throw new IllegalArgumentException(\"expiry time cannot be empty\");\n        }\n\n        int lastIndex =  relativeTime.length() - 1;\n        char lastChar = relativeTime.charAt(lastIndex);\n        final char timeUnit;\n\n        if (!Character.isAlphabetic(lastChar)) {\n            // No unit specified, assume seconds\n            timeUnit = 's';\n            lastIndex = relativeTime.length();\n        } else {\n            timeUnit = Character.toLowerCase(lastChar);\n        }\n\n        long duration = Long.parseLong(relativeTime.substring(0, lastIndex));\n\n        switch (timeUnit) {\n        case 's':","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-common/src/main/java/org/apache/pulsar/common/util/RelativeTimeUtil.java#L15-L51","documentation":"RelativeTimeUtil.parseRelativeTimeInSeconds(relativeTime) converts strings like '1000s', '2h', '3d' into seconds. An empty input string cannot denote any time, so it immediately throws IllegalArgumentException('expiry time cannot be empty'). It is a fail-fast input validation error thrown before any parsing.","triggerScenarios":"Calling RelativeTimeUtil.parseRelativeTimeInSeconds(\"\") — typically from a broker configuration value (e.g. ttl or expiry-style settings) that was left blank.","commonSituations":"broker.conf / client config key present but with an empty value; environment variable expansion yielding an empty string; CLI flag supplied without a value; YAML property defined with no value.","solutions":["Set the configuration value to a valid relative time like '3600s', '24h', or '7d'.","Remove/comment out the empty property so Pulsar applies its default instead of an empty string.","Guard with a default before parsing, e.g. StringUtils.isBlank(value) ? fallback : parseRelativeTimeInSeconds(value).","Use a supported unit suffix (s/m/h/d/w) — note the empty check happens first, then unit validation."],"exampleFix":"// before\nString cfg = config.get(\"expiry\"); // \"\"\nlong secs = RelativeTimeUtil.parseRelativeTimeInSeconds(cfg);\n// after\nString cfg = config.get(\"expiry\");\nlong secs = (cfg == null || cfg.isEmpty()) ? defaultSeconds : RelativeTimeUtil.parseRelativeTimeInSeconds(cfg);","handlingStrategy":"validation","validationCode":"static long safeParse(String cfg, long defaultSeconds) {\n    if (cfg == null || cfg.isEmpty()) return defaultSeconds;\n    return RelativeTimeUtil.parseRelativeTimeInSeconds(cfg);\n}","typeGuard":"static boolean isValidRelativeTime(String s) {\n    if (s == null || s.isEmpty()) return false;\n    char u = s.charAt(s.length() - 1);\n    return \"smhdw\".indexOf(u) >= 0 && s.substring(0, s.length() - 1).chars().allMatch(Character::isDigit);\n}","tryCatchPattern":"try { return RelativeTimeUtil.parseRelativeTimeInSeconds(relativeTime); }\ncatch (IllegalArgumentException e) {\n    if (relativeTime == null || relativeTime.isEmpty())\n        throw new IllegalStateException(\"Configured expiry time is empty; set e.g. '3600s' or remove the property to use the default\", e);\n    throw e;\n}","preventionTips":["Never leave time-related config keys present but blank; remove them to use defaults","Validate configuration at startup with a fail-fast dump of all time-typed values","Use environment variable fallbacks: ${ENV:-default}","Prefer explicit unit suffixes (1000s, 2h, 3d) in all configs"],"tags":["validation","configuration","time-parsing"],"backgroundTag":"invalid-duration-format","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}