Event stream decoder drops the final event when reading it reaches EOF
Maintainers usually reply within 2 days
Assessment
- Difficulty
- 3/5
- Estimated time
- Half a day
- Newbie friendliness
- 62/100
Research direction
Start with valid() in the NonSeekableStreamDecodingEventStreamIterator class, which currently checks the stream's EOF state after the event is parsed. Run the reproduction in the issue against a BufferStream holding one event frame; it should print int(1) once the fix is in. Done means the final event is yielded even when the read hits EOF, and the existing decoder tests still pass. A test using BufferStream rather than php://temp would cover the regression.
Written by the indexing model from the issue text.
Description
Describe the bug
NonSeekableStreamDecodingEventStreamIterator drops the final event when reading it also reaches the end of the stream. If the stream contains just one event, nothing is returned.
valid() checks the stream’s EOF state after the event has already been parsed, so it can return false even though there’s a complete event ready to yield.
Expected Behavior
I think the decoder should still yield the last complete event, even if reading it reached the end of the stream.
Current Behavior
The example below prints int(0) instead of int(1). No exception is thrown.
Reproduction Steps
This example doesn’t need an AWS account or make any network requests.
Install aws/aws-sdk-php version 3.399.2, then run:
<?php
require 'vendor/autoload.php';
use Aws\Api\Parser\NonSeekableStreamDecodingEventStreamIterator;
use GuzzleHttp\Psr7\BufferStream;
$headers = '';
foreach ([
':message-type' => 'event',
':event-type' => 'messageStop',
':content-type' => 'application/json',
] as $name => $value) {
$headers .= chr(strlen($name)).$name.chr(7)
.pack('n', strlen($value)).$value;
}
$payload = '{"stopReason":"end_turn"}';
$prelude = pack('NN', 16 + strlen($headers) + strlen($payload), strlen($headers));
$frame = $prelude.hash('crc32b', $prelude, true).$headers.$payload;
$frame .= hash('crc32b', $frame, true);
$stream = new BufferStream;
$stream->write($frame);
$iterator = new NonSeekableStreamDecodingEventStreamIterator($stream);
var_dump(count(iterator_to_array($iterator)));
Possible Solution
Have valid() check whether there’s a parsed event available, rather than whether the underlying stream has reached EOF.
Additional Information/Context
I also reproduced this through BedrockRuntimeClient::converseStream() using a local HTTP server. It sent messageStop followed by metadata, but only messageStop was returned. Bedrock sends token usage in that final metadata event. ConverseStream documentation.
The existing decoder tests use a php://temp body, which only reports EOF after another read is attempted. BufferStream reports EOF as soon as its last byte is read, exposing the problem.
The same valid() implementation is still present on master. This is separate from #3371; #3373 changes the same decoder to address partial checksum reads.
SDK version used
3.399.2
Environment details
PHP 8.4.25, Ubuntu 24.04.
Generated by Codex, and reviewed by @binaryfire.
- Dominant language
- PHP
- Stars
- 6.2k
- Forks
- 1.3k
- Avg merge
- 2d 7h
- Merged PRs (30d)
- 11
Getting set up
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from aws/aws-sdk-php
-
feature-request needs-triage
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
aws/aws-sdk-php#3365 ·
Maintainers usually reply within 2 days
-
Event stream decoder reports a checksum mismatch when the checksum is read in partsPossibly taken @lazerg claimed this 5 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 25/100
aws/aws-sdk-php#3371 ·
Maintainers usually reply within 2 days
-
AssumeRoleCredentialProvider replaces non-RuntimeException failures with TypeErrorPossibly taken @lazerg claimed this 5 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 25/100
aws/aws-sdk-php#3370 ·
Maintainers usually reply within 2 days
-
announcement
Difficulty 4/5 3-5 days Newbie friendliness 12/100
aws/aws-sdk-php#3369 ·
Maintainers usually reply within 2 days
-
bug needs-triage
Difficulty 4/5 3-5 days Newbie friendliness 30/100
aws/aws-sdk-php#3364 ·
Maintainers usually reply within 2 days
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 66/100
Automattic/blocks-engine#2866 ·
Maintainers usually reply within 1 day
-
enhancement good first issue help wanted
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
-
backend gamification P0
Difficulty 2/5 1-3 hours Newbie friendliness 66/100
semanadeinformatica/website#461 ·
-
HttpRequestTrait fatals on WordPress 7.2: $blocked_http_requests clashes with WP_UnitTestCase_BaseOpen
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
-
area: docs eco: php good first issue help wanted lane: free-agent roadmap size: XS type: docs
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
arminazimi/evergreen#453 ·
Maintainers usually reply within 1 day