{"record":{"id":"3103f845dfec79c9","repo":"grpc/grpc-java","slug":"duration-is-not-valid-see-proto-definition-for-va","errorCode":null,"errorMessage":"Duration is not valid. See proto definition for valid values. Seconds (%s) must be in range [-315,576,000,000, +315,576,000,000]. Nanos (%s) must be in range [-999,999,999, +999,999,999]. Nanos must have the same sign as seconds","messagePattern":"Duration is not valid\\. See proto definition for valid values\\. Seconds \\((.+?)\\) must be in range \\[-315,576,000,000, \\+315,576,000,000\\]\\. Nanos \\((.+?)\\) must be in range \\[-999,999,999, \\+999,999,999\\]\\. Nanos must have the same sign as seconds","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/io/grpc/internal/JsonUtil.java","lineNumber":353,"sourceCode":"   * Copy of {@link com.google.protobuf.util.Durations#normalizedDuration}.\n   */\n  // Math.addExact() requires Android API level 24\n  @SuppressWarnings({\"NarrowingCompoundAssignment\", \"InlineMeInliner\"})\n  private static long normalizedDuration(long seconds, int nanos) {\n    if (nanos <= -NANOS_PER_SECOND || nanos >= NANOS_PER_SECOND) {\n      seconds = checkedAdd(seconds, nanos / NANOS_PER_SECOND);\n      nanos %= NANOS_PER_SECOND;\n    }\n    if (seconds > 0 && nanos < 0) {\n      nanos += NANOS_PER_SECOND; // no overflow— nanos is negative (and we're adding)\n      seconds--; // no overflow since seconds is positive (and we're decrementing)\n    }\n    if (seconds < 0 && nanos > 0) {\n      nanos -= NANOS_PER_SECOND; // no overflow— nanos is positive (and we're subtracting)\n      seconds++; // no overflow since seconds is negative (and we're incrementing)\n    }\n    if (!durationIsValid(seconds, nanos)) {\n      throw new IllegalArgumentException(String.format(\n          \"Duration is not valid. See proto definition for valid values. \"\n              + \"Seconds (%s) must be in range [-315,576,000,000, +315,576,000,000]. \"\n              + \"Nanos (%s) must be in range [-999,999,999, +999,999,999]. \"\n              + \"Nanos must have the same sign as seconds\", seconds, nanos));\n    }\n    return saturatedAdd(TimeUnit.SECONDS.toNanos(seconds), nanos);\n  }\n\n  /**\n   * Returns true if the given number of seconds and nanos is a valid {@code Duration}. The {@code\n   * seconds} value must be in the range [-315,576,000,000, +315,576,000,000]. The {@code nanos}\n   * value must be in the range [-999,999,999, +999,999,999].\n   *\n   * <p><b>Note:</b> Durations less than one second are represented with a 0 {@code seconds} field\n   * and a positive or negative {@code nanos} field. For durations of one second or more, a non-zero\n   * value for the {@code nanos} field must be of the same sign as the {@code seconds} field.\n   *\n   * <p>Copy of {@link com.google.protobuf.util.Duration#isValid}.</p>","sourceCodeStart":335,"sourceCodeEnd":371,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/core/src/main/java/io/grpc/internal/JsonUtil.java#L335-L371","documentation":"JsonUtil.normalizedDuration converts a proto JSON Duration (seconds + nanos) into a single nanoseconds long. Before combining, it validates that seconds fit within the proto Duration range (±315,576,000,000) and nanos within ±999,999,999 with matching sign. If durationIsValid fails, it throws this IllegalArgumentException because the resulting value cannot be represented as a valid proto duration.","triggerScenarios":"Parsing a JSON/proto Duration via parseDuration where the input has seconds outside ±315,576,000,000, nanos outside ±999,999,999, or nanos with a sign opposite to seconds after normalization (e.g. seconds=5 and nanos=-500 when |seconds| >= the range limit).","commonSituations":"Hand-written channel/service config JSON with a malformed duration string like '99999999999999999s', or durations constructed programmatically with mismatched sign components; also duration strings exceeding ~10,000 years.","solutions":["Fix the duration string/value in the config so seconds is within ±315,576,000,000 and nanos within ±999,999,999 with the same sign (e.g. '5s' not '5s-500ns').","Parse with a standard format like '3.000000001s' (protobuf JSON duration syntax) instead of hand-assembling seconds/nanos.","If computing durations programmatically, clamp or saturate values before passing them into JSON parsing."],"exampleFix":"// before\n{\"methodConfig\": [{\"timeout\": \"99999999999999s\"}]}\n// after\n{\"methodConfig\": [{\"timeout\": \"30s\"}]}","handlingStrategy":"validation","validationCode":"boolean validDuration(String d) {\n  java.util.regex.Matcher m = java.util.regex.Pattern.compile(\"^(-?\\\\d+)(?:\\\\.(\\\\d{1,9}))?s$\").matcher(d);\n  if (!m.matches()) return false;\n  long sec = Long.parseLong(m.group(1));\n  int nanos = m.group(2) == null ? 0 : Integer.parseInt((m.group(2) + \"000000000\").substring(0, 9)) * (sec < 0 ? -1 : 1);\n  return Math.abs(sec) <= 315576000000L && Math.abs(nanos) <= 999999999;\n}\n// use: if (!validDuration(timeoutStr)) throw new IllegalArgumentException(\"bad timeout: \" + timeoutStr);","typeGuard":null,"tryCatchPattern":"try {\n  parseConfig(json);\n} catch (IllegalArgumentException e) {\n  if (e.getMessage() != null && e.getMessage().contains(\"Duration is not valid\")) {\n    log.warn(\"Invalid duration in config, using default\");\n  } else throw e;\n}","preventionTips":["Always emit durations in protobuf JSON form like '30s' or '1.500s'.","Keep nanos and seconds the same sign; prefer a single signed string.","Bound durations to realistic values (< 315,576,000,000 seconds)."],"tags":["duration","validation","grpc","config-parsing"],"backgroundTag":"invalid-duration-format","analyzedSha":"64daddc1f3d1975670f769f3e97bde8b2ba32d25","analyzedAt":"2026-09-08T06:14:57.704Z","contentChangedAt":"2026-09-08T06:14:57.704Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}