Skip to content

[core] fix None yield from restarted streaming generator with application errors - #65121

Open
rueian wants to merge 12 commits into
ray-project:masterfrom
rueian:fix-streaming-generator-retry-none
Open

[core] fix None yield from restarted streaming generator with application errors#65121
rueian wants to merge 12 commits into
ray-project:masterfrom
rueian:fix-streaming-generator-retry-none

Conversation

@rueian

@rueian rueian commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Description

Previously, there was a special path in the streaming generator reconstruction:

If the retried streaming generator stops with an application exception, it will try to fill return_objects(0) for all refs that have not yet been restored. But return_objects(0) is None if the first streaming generator succeeds.

The result is that, in the case of lineage reconstruction, we can see ray.get(ref) unexpectedly return None if the restarted streaming generator stops with an application exception.

This PR removes the special path. We will still treat the exception as the recovered object for the corresponding ref, but if the restarted streaming generator produces a different number of yields than its first execution, the user will get a StreamingGeneratorReplayInconsistentError exception on the following ray.get instead.

@rueian rueian added core Issues that should be addressed in Ray Core go add ONLY when ready to merge, run all tests labels Jul 30, 2026
@rueian rueian changed the title [core] fix None yield from restarted streaming generator with applica… [core] fix None yield from restarted streaming generator with application errors Jul 30, 2026
…tion errors

Signed-off-by: Rueian Huang <rueiancsie@gmail.com>
@rueian
rueian force-pushed the fix-streaming-generator-retry-none branch from b705c88 to 81b3443 Compare July 30, 2026 02:48
Signed-off-by: Rueian Huang <rueiancsie@gmail.com>
@rueian
rueian marked this pull request as ready for review July 30, 2026 23:00
@rueian
rueian requested a review from a team as a code owner July 30, 2026 23:00
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request ensures that streaming generator replays failing with an application error are correctly validated for object count consistency, and prevents copying the static None return onto stream ObjectRefs during such replays. Specifically, FailStreamingGeneratorReplayIfInconsistent is now executed for both successful and application-error completions. Additionally, regression and unit tests have been added to verify these behaviors. There are no review comments, so I have no feedback to provide.

Signed-off-by: Rueian Huang <rueiancsie@gmail.com>

@Kunchd Kunchd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the fix! I'm a little confused on the exact desired behavior that we want here, so I left a few questions.

Comment thread src/ray/core_worker/task_manager.h Outdated
// the inconsistent objects before the failure propagates. Applies to both
// successful and application-error completions: retries must reproduce the
// same object count, and app-error replays often report fewer IDs.
if (FailStreamingGeneratorReplayIfInconsistent(task_id, reply)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If FailStreamingGeneratorReplayIfInconsistent returns true, it means one of the following:

  • An error (application or system level) has caused the generator to terminate before it could reach the same point as the first execution. In this case, should we return the actual error that caused the issue instead of STREAMING_GENERATOR_REPLAY_INCONSISTENT?
  • The generator was non-deterministic and returned a different number of results. In this case, we should return STREAMING_GENERATOR_REPLAY_INCONSISTENT.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we only deal with application errors. System-level failures are handled via FailPendingTask, which doesn't have the FailStreamingGeneratorReplayIfInconsistent check.

The actual application error should still be observed by users since an application error will be treated as a normal yield. For example, if the user has ref1 and ref2 originated from a streaming generator on hand but the actual objects are lost, and the reconstructed streaming generator fails with an Exception1 before yielding anything. Then ray.get(ref1) will raise Exception1 and ray.get(reg2) will raise STREAMING_GENERATOR_REPLAY_INCONSISTENT

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the application error is non-retriable, I don't think we actually report the application error as a generator yield (see https://github.com/ray-project/ray/blob/master/python/ray/_raylet.pyx#L1911-L1925). Instead the error is stored as a return value: https://github.com/ray-project/ray/blob/master/python/ray/_raylet.pyx#L2411-L2420. In this case, wouldn't we be missing this error if we don't do what we did in the past and explicitly populate the streaming generator with the error?

Also, maybe I'm missing something, but I don't know if FailPendingTask is guaranteed to trigger on system failures. It seems like we only report a non-ok status for system errors if the output was not written (see https://github.com/ray-project/ray/blob/master/src/ray/core_worker/task_execution/task_receiver.cc#L123-L133). I'm not sure if it's possible for the output to be correctly written, but we still receive a system error afterwards.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the application error is non-retriable, we do report it as a yield. ray.get(gen.completed()) will be None though. The user can only get the error via ray.get(next(gen)).

If the application error is retriable, the expectation is that we never report it as a yield. The user can only get the error via ray.get(gen.completed()) after the retries are exhausted.

In this case, wouldn't we be missing this error if we don't do what we did in the past and explicitly populate the streaming generator with the error?

We will only miss an application error if it meets both of the following:

  1. It is retriable so we don't report it as a yield.
  2. The streaming generator has succeeded once so it can't be injected into gen.completed().

At this point, I think we still shouldn't make the retriable application error a yield. That is not aligned with the expectation of marking an error retriable. Users should still expect to only get the retriable error via ray.get(gen.completed()) after the retries are exhausted. This has nothing to do with the FailStreamingGeneratorReplayIfInconsistent.

Also, maybe I'm missing something, but I don't know if FailPendingTask is guaranteed to trigger on system failures. It seems like we only report a non-ok status for system errors if the output was not written (see https://github.com/ray-project/ray/blob/master/src/ray/core_worker/task_execution/task_receiver.cc#L123-L133). I'm not sure if it's possible for the output to be correctly written, but we still receive a system error afterwards.

I think we really don't care about the system errors in the path of task_receiver.cc#L123-L133 because when objects_valid is true for a streaming generator, it means the streaming generator has ended already by itself, not by the system error. So if it has inconsistent yields, then we should raise STREAMING_GENERATOR_REPLAY_INCONSISTENT.

Comment thread src/ray/core_worker/task_manager.h Outdated
rueian added 2 commits August 4, 2026 20:23
Signed-off-by: Rueian Huang <rueiancsie@gmail.com>
…ator-retry-none

Keep master's first-execution EOF error materialization, and drop the
re-execution app-error path that copied return_objects(0) onto stream refs.

Signed-off-by: Rueian Huang <rueiancsie@gmail.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

Reviewed by Cursor Bugbot for commit 768956c. Configure here.

Comment thread src/ray/core_worker/task_manager.cc

@Kunchd Kunchd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for clarifying! I left one last comment from my interpretation of what's expected of the test.

app_error_reply.add_streaming_generator_return_ids();
return_id_proto->set_object_id(spec.StreamingGeneratorReturnId(i).Binary());
return_id_proto->set_is_plasma_object(true);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One final comment. From my walk through the code, the current invocations in the test doesn't fully replicate the states for a resubmission. When an error is reported, we should actually invoke HandleReportGeneratorItemReturns on them as well. And this should in turn cause the error to be added to plasma via put_in_local_plasma_callback and the assertion for plasma_put_error_types should not be false below.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core Issues that should be addressed in Ray Core go add ONLY when ready to merge, run all tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants