Fix NPE in TryExecutor when retry policy omits optional fields#1519
Fix NPE in TryExecutor when retry policy omits optional fields#1519mcruzdev wants to merge 2 commits into
Conversation
The spec defines backoff, limit, and delay as optional in a retry policy, but the runtime assumed all three were always present, causing NullPointerException when any was omitted. - Default to constant backoff when backoff is not specified - Default to unlimited retries when limit is not specified - Default to zero delay when delay is not specified Signed-off-by: Matheus Cruz <matheuscruz.dev@gmail.com>
There was a problem hiding this comment.
Pull request overview
This PR addresses runtime NullPointerException failures when a RetryPolicy omits optional fields (as allowed by the spec) by adding defaulting/guard logic in the retry executor path and adding regression tests for common omission scenarios.
Changes:
- Default retry backoff to constant backoff when
retryPolicy.backoffis omitted. - Default retry limit to a max when
retryPolicy.limit/limit.attemptis omitted. - Default retry delay to
Duration.ZEROwhenretryPolicy.delayis omitted. - Add fluent DSL tests covering retry behavior when
backoff,limit, ordelayare omitted.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| impl/core/src/main/java/io/serverlessworkflow/impl/executors/TryExecutor.java | Adds resolveMaxAttempts() and defaults missing backoff to constant backoff. |
| impl/core/src/main/java/io/serverlessworkflow/impl/executors/retry/AbstractRetryIntervalFunction.java | Defaults missing delay to zero duration via a constant resolver. |
| experimental/test/src/test/java/io/serverlessworkflow/fluent/test/FuncTryCatchTest.java | Adds regression tests for retry policies missing backoff, limit, or delay. |
Comments suppressed due to low confidence (1)
impl/core/src/main/java/io/serverlessworkflow/impl/executors/retry/AbstractRetryIntervalFunction.java:42
- The constructor still dereferences
jitter.getFrom()/jitter.getTo()without guarding against them being null. The fluent builder allows setting only one of these, so this path can still throw a NullPointerException even with the top-leveljitter != nullcheck. Suggest treating missingfrom/toas no jitter (Duration.ZERO).
if (jitter != null) {
minJitteringResolver = Optional.of(WorkflowUtils.fromTimeoutAfter(appl, jitter.getFrom()));
maxJitteringResolver = Optional.of(WorkflowUtils.fromTimeoutAfter(appl, jitter.getTo()));
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The retry limit default was set to Integer.MAX_VALUE, but when no limit is configured the retry should not execute. Also update the test to verify the workflow fails when no retry limit is set. Signed-off-by: Matheus Cruz <matheuscruz.dev@gmail.com>
| private static final WorkflowValueResolver<Duration> ZERO_DURATION_RESOLVER = | ||
| (w, t, m) -> Duration.ZERO; | ||
|
|
There was a problem hiding this comment.
Lets move this one to WorkflowUtils and reference both in WorkflowUtils and here.
| if (limit == null || limit.getAttempt() == null) { | ||
| return 0; | ||
| } | ||
| return limit.getAttempt().getCount(); |
There was a problem hiding this comment.
| if (limit == null || limit.getAttempt() == null) { | |
| return 0; | |
| } | |
| return limit.getAttempt().getCount(); | |
| return limit != null && limit.getAttempt() != null ? limit.getAttempt().getCount() : 0; |
fjtirado
left a comment
There was a problem hiding this comment.
Just usual picky comments ;)
The spec defines backoff, limit, and delay as optional in a retry policy, but the runtime assumed all three were always present, causing NullPointerException when any was omitted.
Closes #1517