Skip to content
This repository was archived by the owner on Jun 3, 2026. It is now read-only.

fix: handle empty Bedrock stream response (messageStop without messageStart) - #749

Open
pfolegmalkov wants to merge 2 commits into
strands-agents:mainfrom
pfolegmalkov:fix/empty-stream-response
Open

fix: handle empty Bedrock stream response (messageStop without messageStart)#749
pfolegmalkov wants to merge 2 commits into
strands-agents:mainfrom
pfolegmalkov:fix/empty-stream-response

Conversation

@pfolegmalkov

Copy link
Copy Markdown

Problem

When using AWS Bedrock Converse API, the model may return a modelMessageStopEvent without a preceding modelMessageStartEvent. This happens when the model produces an empty response — for example, after processing certain tool results (like ask_clarifying_questions) where the model determines it has nothing to add.

The current code throws ModelError('Stream ended without completing a message') because stoppedMessage is null (set only when messageRole is truthy, which requires modelMessageStartEvent).

This is valid Bedrock behavior — the Converse API spec allows messageStop events with a stop reason even when no content blocks were produced.

Fix

In src/models/model.ts, the modelMessageStopEvent handler now defaults to 'assistant' role when modelMessageStartEvent was not received:

// Before
if (messageRole) {
  stoppedMessage = new Message({ role: messageRole, content: [...contentBlocks] })
  finalStopReason = event.stopReason!
}

// After
stoppedMessage = new Message({ role: messageRole ?? 'assistant', content: [...contentBlocks] })
finalStopReason = event.stopReason!

The contentBlocks array will be empty in this case, which is the correct representation of an empty model response.

Test plan

Added 4 new test cases in src/models/__tests__/model.test.ts:

  • messageStop without messageStart → creates assistant message with empty content
  • content blocks before messageStop without messageStart → preserves accumulated blocks
  • toolUse stopReason without messageStart → handled correctly
  • completely empty stream → still throws ModelError (no events at all = real error)

All 1617 tests pass. Coverage on model.ts branch coverage improved (87.09% → 90.32%).

Reproduction

This issue was discovered in a production conversational search application using Bedrock Converse API with Claude Sonnet 4.6 and 13 tools. The model consistently returns empty responses after ask_clarifying_questions tool results, causing the agent loop to crash.

Checklist

  • Tests added
  • All existing tests pass (1617/1617)
  • Pre-commit hooks pass (lint, format, type-check)
  • No breaking changes — empty responses that previously threw now return a valid empty Message

…eStart)

Bedrock Converse API may return modelMessageStopEvent without a preceding
modelMessageStartEvent when the model produces an empty response (e.g., after
certain tool results like ask_clarifying_questions). Previously this threw
'Stream ended without completing a message'. Now defaults to assistant role
with empty content array, matching Bedrock's behavior.
4 new test cases in model.test.ts:
- messageStop without messageStart creates assistant message with empty content
- content blocks accumulated before messageStop without messageStart are preserved
- toolUse stopReason without messageStart handled correctly
- completely empty stream (no events) still throws ModelError

Covers the fix in model.ts where modelMessageStopEvent now defaults to
'assistant' role when modelMessageStartEvent was not received.
@strands-agent

Copy link
Copy Markdown
Collaborator

This repository has been merged into the strands-agents/harness-sdk monorepo and will be archived shortly. All new development happens there.

If this PR is still relevant, please recreate it against the monorepo. The code now lives under strands-ts/. Full commit history was preserved, so your base should be findable.

Apologies for the disruption, and thank you for contributing!

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants