sangkyoonnam opened a new issue, #1170:
URL: https://github.com/apache/flink-agents/issues/1170

   ### Search before asking
   
   - [x] I searched in the 
[issues](https://github.com/apache/flink-agents/issues) and found nothing 
similar.
   
   ### Description
   
   A durable call whose callable returns `null` is recorded as a success with 
no result payload, and replaying it throws `NullPointerException`: 
`RunnerContextImpl#tryGetCachedResult` returns `Optional.of(null)` for that 
slot. When recovery replays an unfinished action and reaches this recorded 
call, it throws again on every attempt; if the exception escapes the action, 
the action can't be recovered from that saved state.
   
   The built-in async sub-agent path hits it. 
`BaseAsyncSubagentSetup#submitRequest` is a `DurableCallable<Void>` with a 
reconciler, run through `durableExecuteAsync`, and `ToolCallAction` submits the 
sub-agent and awaits its result in the same action. If the job fails over while 
the action waits, the replayed submit throws; `ToolCallAction` catches it and 
reports the sub-agent as failed, so the wait for the remote run, which was 
already started, never resumes.
   
   `serializeDurableResult(null)` returns `null`, so the slot is stored as 
SUCCEEDED with neither payload. Both callers of `tryGetCachedResult` hit it: 
`durableExecuteCompletionOnly` (`durableExecute` or `durableExecuteAsync` 
without a reconciler) and the terminal-slot branch of 
`durableExecuteWithReconcile`. `readTerminalOutcomeAt`, used by the Java batch 
`gather`, already returns `Outcome.success(null)` for the same slot. The Python 
runtime is unaffected; its cache lookup keeps the hit flag separate from the 
value.
   
   Expected: replay returns `null` without running the callable or the 
reconciler again, and the recorded call is not duplicated.
   
   #1155 (open) changes the signature of `tryGetCachedResult` but keeps this 
branch, so a fix and #1155 touch the same method. I'm happy to rebase onto it 
if it lands first.
   
   ### How to reproduce
   
   ```java
   // Inside an action: a Void side effect.
   ctx.durableExecute(voidCallable);   // returns null; slot recorded as 
SUCCEEDED with no payload
   
   // The job fails over and the action replays from the saved action state:
   ctx.durableExecute(voidCallable);
   // java.lang.NullPointerException
   //     at java.base/java.util.Objects.requireNonNull(Objects.java:233)
   //     at java.base/java.util.Optional.of(Optional.java:113)
   //     at 
org.apache.flink.agents.runtime.context.RunnerContextImpl.tryGetCachedResult(RunnerContextImpl.java:933)
   //     at 
org.apache.flink.agents.runtime.context.RunnerContextImpl.durableExecuteCompletionOnly(RunnerContextImpl.java:624)
   //     ...
   ```
   
   With a reconciler, as in the sub-agent path, the same slot fails through 
`durableExecuteWithReconcile` (line 982).
   
   ### Version and environment
   
   main (`2e97add7`). JDK 21, OS independent. Java runtime only.
   
   ### Are you willing to submit a PR?
   
   - [x] I'm willing to submit a PR!


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to