Skip to content

Fix/bedrock empty content error context#2153

Closed
aleksmeshr wants to merge 2 commits into
0xPlaygrounds:mainfrom
aleksmeshr:fix/bedrock-empty-content-error-context
Closed

Fix/bedrock empty content error context#2153
aleksmeshr wants to merge 2 commits into
0xPlaygrounds:mainfrom
aleksmeshr:fix/bedrock-empty-content-error-context

Conversation

@aleksmeshr

Copy link
Copy Markdown
Contributor

No description provided.

…th stop_reason

Previously, when Bedrock returned an assistant message with zero content
blocks (e.g. guardrail intervention, content filter, max_tokens
exhaustion during extended thinking, or model glitch), rig-bedrock
raised `CompletionError::RequestError(EmptyListError)` deep inside the
`OneOrMany::many` conversion in `message.rs`. This is misleading on two
counts:

  1. The `RequestError` variant is documented as "Error building the
     completion request" — it is for outgoing request construction
     failures, not response parsing.
  2. The bare `EmptyListError` carries no context about *why* the
     response was empty, making it impossible for callers to branch
     (retry on max_tokens vs. fail fast on guardrails/content filter).

Changes:

- Short-circuit empty assistant content in `TryFrom<AwsConverseOutput>`
  with a `ResponseError` that includes `stop_reason` context.
- Re-label `OneOrMany::many` failures in `TryFrom<aws_bedrock::Message>`
  from `RequestError` to `ResponseError`.
- Add `Display` impl for `StopReason` so the converse stop reason can be
  formatted into error messages (`max_tokens`, `guardrail_intervened`,
  `content_filtered`, `end_turn`, etc.).

This lets downstream code distinguish recoverable cases (e.g. retry
with doubled `max_tokens` when `stop_reason=max_tokens`) from terminal
cases (guardrails, content filter) instead of substring-matching on
`"empty vector"` in the error message.
…ty content

When Bedrock returns an assistant message with zero content blocks AND
`stop_reason=end_turn`, treat it as a benign empty response by
synthesizing a single empty `AssistantContent::Text("")` block, so
callers receive a well-formed `CompletionResponse` instead of an error.

Other stop reasons (max_tokens, guardrail_intervened, content_filtered,
stop_sequence, tool_use, unknown) still return a `ResponseError`
carrying the stop_reason context so callers can branch (e.g. retry on
max_tokens, fail fast on guardrails).

Adds three unit tests covering end_turn, max_tokens, and guardrail
empty-content scenarios.
@aleksmeshr aleksmeshr closed this Jul 15, 2026
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.

1 participant