{"record":{"id":"8d7775cedb4f6d2e","repo":"apache/hadoop","slug":"max-relative-expiry-is-negative","errorCode":null,"errorMessage":"Max relative expiry is negative.","messagePattern":"Max relative expiry is negative\\.","errorType":"exception","errorClass":"InvalidRequestException","httpStatus":null,"severity":"error","filePath":"hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/protocol/CachePoolInfo.java","lineNumber":232,"sourceCode":"        hashCode();\n  }\n\n  public static void validate(CachePoolInfo info) throws IOException {\n    if (info == null) {\n      throw new InvalidRequestException(\"CachePoolInfo is null\");\n    }\n    if ((info.getLimit() != null) && (info.getLimit() < 0)) {\n      throw new InvalidRequestException(\"Limit is negative.\");\n    }\n    if ((info.getDefaultReplication() != null)\n            && (info.getDefaultReplication() < 0)) {\n      throw new InvalidRequestException(\"Default Replication is negative\");\n    }\n\n    if (info.getMaxRelativeExpiryMs() != null) {\n      long maxRelativeExpiryMs = info.getMaxRelativeExpiryMs();\n      if (maxRelativeExpiryMs < 0l) {\n        throw new InvalidRequestException(\"Max relative expiry is negative.\");\n      }\n      if (maxRelativeExpiryMs > Expiration.MAX_RELATIVE_EXPIRY_MS) {\n        throw new InvalidRequestException(\"Max relative expiry is too big.\");\n      }\n    }\n    validateName(info.poolName);\n  }\n\n  public static void validateName(String poolName) throws IOException {\n    if (poolName == null || poolName.isEmpty()) {\n      // Empty pool names are not allowed because they would be highly\n      // confusing.  They would also break the ability to list all pools\n      // by starting with prevKey = \"\"\n      throw new IOException(\"invalid empty cache pool name\");\n    }\n  }\n}\n","sourceCodeStart":214,"sourceCodeEnd":250,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/protocol/CachePoolInfo.java#L214-L250","documentation":"CachePoolInfo.validate() throws when a pool's maxRelativeExpiryMs is set and negative. Max relative expiry bounds how far in the future directives in the pool may set their expiration; a negative duration is meaningless.","triggerScenarios":"addCachePool/modifyCachePool with setMaxRelativeExpiryMs(negative), e.g. computing 'now - lastExpiry' style values or unit conversion mistakes (passing a negative millisecond delta).","commonSituations":"Time arithmetic with System.currentTimeMillis() differences that can go negative, or passing seconds where milliseconds are expected via a negative sentinel.","solutions":["Use a non-negative duration; use Expiration.MAX_RELATIVE_EXPIRY_MS for 'never expire'.","Clamp computed expiries: long expiry = Math.max(0L, computedExpiryMs);","Validate units (ms, not s) before calling setMaxRelativeExpiryMs."],"exampleFix":"// before\ninfo.setMaxRelativeExpiryMs(endTimeMs - System.currentTimeMillis()); // can be negative\n\n// after\nlong rel = Math.max(0L, endTimeMs - System.currentTimeMillis());\ninfo.setMaxRelativeExpiryMs(rel);","handlingStrategy":"validation","validationCode":"long maxExpiry = Math.max(0L, requestedExpiryMs);\ninfo.setMaxRelativeExpiryMs(maxExpiry);","typeGuard":null,"tryCatchPattern":"try { dfs.addCachePool(info); }\ncatch (InvalidRequestException e) { /* reject the expiry input at the API boundary */ }","preventionTips":["Compute relative expiries as Math.max(0, end - now) — time deltas can go negative on clock skew.","Keep all expiry math in milliseconds and name variables accordingly (…Ms).","Validate external expiration input before it reaches CachePoolInfo."],"tags":["hdfs","caching","validation","expiration"],"backgroundTag":"input-validation-failed","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}