Skip to content

fix(sdk): filter batch items on status alone - #765

Open
vyrnsynx wants to merge 3 commits into
aws:mainfrom
vyrnsynx:fix/batch-result-status-filters
Open

vyrnsynx wants to merge 3 commits into
aws:mainfrom
vyrnsynx:fix/batch-result-status-filters

Conversation

@vyrnsynx

@vyrnsynx vyrnsynx commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Issue #, if available:: Closes #760

Description of changes:

succeeded() and get_results() also checked item.result is not None, so a branch that returned None was counted in success_count but dropped from both. failed() and get_errors() had the same check on item.error.

Following the approach suggested in #760:

  • succeeded() and failed() select items by status alone. get_results() and get_errors() are built from them with a cast, so the return types stay list[R] and list[ErrorObject]
  • When replay rebuilds a FAILED item from a child context checkpoint that has no error (_replay_terminal_item and _replay_from_checkpoints), it now records the generic ChildContextError that replaying that child context raises. Every FAILED item has an error, and throw_if_error() raises for it
  • test_batch_result_failed_with_none_error asserted the old filter, so it now expects the FAILED item to be kept

For the release notes: get_results() can now contain None when a branch returns nothing, so something like sum(batch.get_results()) raises in that case.

Testing:

  • New tests fail on main and pass with the change: BatchResult with None results, a live execute() over branches returning [None, 10, None, 30], and both replay paths with a FAILED checkpoint that has no error
  • hatch run dev-core:cov: 1769 passed, 98.23% coverage
  • hatch run test:all: 4080 passed, 2 skipped
  • hatch run types:check and hatch fmt --check in the core package: passed

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

succeeded() and get_results() also required a non-None result, so a
branch that returned None counted towards success_count but was
missing from both. failed() and get_errors() had the same check on
the error.

Select items by status only. When replay rebuilds a FAILED item from a
child context checkpoint without an error, record the generic
ChildContextError that replaying that child context raises, so every
FAILED item still has an error.

Closes aws#760
@vyrnsynx
vyrnsynx deployed to ai-pr-review-runtime October 9, 2026 06:17 — with GitHub Actions Active
for item in self.all
if item.status is BatchItemStatus.FAILED and item.error is not None
]
return [cast("ErrorObject", item.error) for item in self.failed()]

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.

cast() only changes static typing; it leaves None unchanged. BatchItem(..., FAILED, error=None) remains accepted (including in test_batch_result_failed_with_none_error), and BatchResult.from_dict() also permits it. These paths bypass _failed_checkpoint_error_object(), so get_errors() can return [None] despite its list[ErrorObject] contract. I reproduced this after a default ExtendedTypeSerDes round trip; accessing error.type then raises AttributeError.

Could we normalize missing failed-item errors at the model/deserialization boundary using the same generic ChildContextError, and extend the regression test to cover get_errors() after deserialization? That would preserve one error per failed item while making the return type accurate.

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.

Good catch — cast() was only papering over the typing hole.

I normalized missing FAILED-item errors at the model boundary (BatchItem.__post_init__ / from_dict), using the same generic ChildContextError message the executor already records via _failed_checkpoint_error_object when a child-context checkpoint has no ErrorObject. get_errors() still falls back defensively.

Extended test_batch_result_failed_with_none_error to assert get_errors() after construction, after from_dict of an error: null wire payload, and after an ExtendedTypeSerDes round-trip of that same null-error payload (the path that was raising AttributeError on error.type).

FAILED BatchItems constructed or deserialized without an ErrorObject
now receive the same generic ChildContextError the executor records for
a FAILED child-context checkpoint with no error. Extends the regression
test to cover get_errors() after from_dict and ExtendedTypeSerDes.

Addresses review feedback on aws#765.

This branch was successfully deployed

1 active (outdated) deployment
ai-pr-review-runtime — cdae7556 Deployed Oct 9, 2026 by vyrnsynx via notify / Generate notification summary #191
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: BatchResult.succeeded() and get_results() drop succeeded items whose result is None

2 participants