{"record":{"id":"b60927ffd209b454","repo":"apache/beam","slug":"work-completed-and-work-remaining-must-be-greater-than-or","errorCode":null,"errorMessage":"Work completed and work remaining must be greater than or equal to zero but were %s and %s.","messagePattern":"Work completed and work remaining must be greater than or equal to zero but were (.+?) and (.+?)\\.","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/splittabledofn/RestrictionTracker.java","lineNumber":191,"sourceCode":"   * A representation for the amount of known completed and remaining work. See {@link\n   * HasProgress#getProgress()} for details.\n   */\n  @AutoValue\n  public abstract static class Progress {\n\n    /** Constant Progress instance to be used when no work has been completed yet. */\n    public static final Progress NONE = from(0, 1);\n\n    /**\n     * A representation for the amount of known completed and remaining work. See {@link\n     * HasProgress#getProgress()} for details.\n     *\n     * @param workCompleted Must be {@code >= 0}.\n     * @param workRemaining Must be {@code >= 0}.\n     */\n    public static Progress from(double workCompleted, double workRemaining) {\n      if (workCompleted < 0 || workRemaining < 0) {\n        throw new IllegalArgumentException(\n            String.format(\n                \"Work completed and work remaining must be greater than or equal to zero but were %s and %s.\",\n                workCompleted, workRemaining));\n      }\n      return new AutoValue_RestrictionTracker_Progress(workCompleted, workRemaining);\n    }\n\n    /** The known amount of completed work. */\n    public abstract double getWorkCompleted();\n\n    /** The known amount of work remaining. */\n    public abstract double getWorkRemaining();\n  }\n\n  /** A representation of the truncate result. */\n  @AutoValue\n  public abstract static class TruncateResult<RestrictionT> {\n    /** Returns a {@link TruncateResult} for the given restriction. */","sourceCodeStart":173,"sourceCodeEnd":209,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/splittabledofn/RestrictionTracker.java#L173-L209","documentation":"RestrictionTracker.Progress.from builds an immutable progress snapshot. It validates that workCompleted and workRemaining are non-negative; a negative value throws IllegalArgumentException with both numbers in the message. This guards the contract documented on the method's parameters.","triggerScenarios":"Calling RestrictionTracker.Progress.from(x, y) with x < 0 or y < 0, typically from a custom RestrictionTracker.getProgress() implementation computing negative work (e.g. subtraction underflow or NaN-adjacent logic bugs).","commonSituations":"Implementing a custom RestrictionTracker where the work-completed calculation subtracts a larger value from a smaller one; float rounding producing negative residuals; misconfigured restriction sizes.","solutions":["Clamp computed values: Math.max(0, workCompleted) and Math.max(0, workRemaining) before calling from()","Audit the custom tracker's progress math for underflow or wrong ordering of subtraction operands","Validate restriction size > 0 when constructing the tracker so progress never goes negative","Handle NaN/rounding explicitly if work is derived from floating-point positions"],"exampleFix":"// before\ndouble done = position - startPos; // can be negative on wrap-around\nreturn Progress.from(done, endPos - position);\n\n// after\ndouble done = Math.max(0, position - startPos);\ndouble remaining = Math.max(0, endPos - position);\nreturn Progress.from(done, remaining);","handlingStrategy":"validation","validationCode":"if (workCompleted < 0 || workRemaining < 0 || Double.isNaN(workCompleted) || Double.isNaN(workRemaining)) {\n  throw new IllegalArgumentException(\"progress must be non-negative\");\n}","typeGuard":"static boolean isValidProgress(double done, double remaining) {\n  return done >= 0 && remaining >= 0 && !Double.isNaN(done) && !Double.isNaN(remaining);\n}","tryCatchPattern":"try {\n  return Progress.from(done, remaining);\n} catch (IllegalArgumentException e) {\n  return Progress.from(0, 0); // or clamp inputs and retry\n}","preventionTips":["Clamp all computed progress values with Math.max(0, x)","Check subtraction order in custom tracker progress math","Unit-test custom RestrictionTracker.getProgress at boundary positions"],"tags":["java","apache-beam","restriction-tracker","negative-value","argument-validation"],"backgroundTag":"value-out-of-range","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-14T21:17:11.552Z"}