Skip to content

fixed message start frame - #5907

Merged
akshaydeo merged 1 commit into
devfrom
08-06-fixed_message_start_frame
Aug 6, 2026
Merged

fixed message start frame#5907
akshaydeo merged 1 commit into
devfrom
08-06-fixed_message_start_frame

Conversation

@akshaydeo

@akshaydeo akshaydeo commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Strict Anthropic-dialect clients (e.g. @ai-sdk/anthropic) validate the very first SSE frame and abort the stream if message_start is missing a message object, message.usage, or message.content. This was causing streams to fail when Bifrost served requests backed by Bedrock Converse or any provider routed through SendCreatedEventResponsesChunk, which have no usage figures at message_start time. Additionally, the Chat Completions → Anthropic converter could panic on a content-less assistant message.

Changes

  • Responses converter (responses.go): The message object is now built unconditionally on message_start, even when the upstream created event carries no BifrostResponsesResponse payload. Usage defaults to all-zeros (neutral under both additive and max-merge accounting) when real counts are not yet available. Nil guards were added for Response.ID, Response.Usage, and Response.Diagnostics.
  • Chat converter (chat.go): message_start frames now always include an empty Usage struct and an initialized (non-nil) content array. The role is read defensively from the message when present, falling back to assistant. The model is only attached to the message object when one already exists (i.e. on message_start), preventing stub objects from being emitted on delta frames. Usage is placed under message.usage on message_start and at the top level on message_delta, matching the Anthropic wire protocol.
  • Tests (messagestartusage_test.go): Consolidated and extended into a shared requireValidMessageStart helper that asserts the marshalled wire shape (type, message object, usage object with both token fields, content array) for both the Responses and Chat Completions converters. Added a case for a created event with no Response payload at all.

Type of change

  • Bug fix

Affected areas

  • Core (Go)
  • Providers/Integrations

How to test

go test ./core/providers/anthropic/... -run TestResponsesMessageStartAlwaysCarriesUsage
go test ./core/providers/anthropic/... -run TestChatMessageStartAlwaysCarriesUsage
go test ./...

Send a streaming /v1/messages request routed through a Bedrock Converse model using an @ai-sdk/anthropic client and confirm the stream completes without a Type validation failed ... path: [message, usage] error on the first frame.

Breaking changes

  • No

Related issues

Closes #5885

Security considerations

None.

Checklist

  • I read docs/contributing/README.md and followed the guidelines
  • I added/updated tests where appropriate
  • I updated documentation where needed
  • I verified builds succeed (Go and UI)
  • I verified the CI pipeline passes locally if applicable

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes
    • Improved Anthropic streaming responses to consistently include valid message, usage, and content data.
    • Ensured message_start events include initialized usage and content fields, even when information is unavailable.
    • Prevented malformed events and errors when response details or usage information are missing.
    • Preserved available model metadata and diagnostics during streaming.
    • Enhanced compatibility across Chat Completions and Responses conversion paths.
    • Added safeguards for content-less assistant messages and incomplete response data.

Walkthrough

The Anthropic Responses and Chat Completions converters now emit valid message_start frames when response data is missing or incomplete. Regression tests validate message, usage, token fields, content arrays, and SSE payloads.

Changes

Anthropic message_start framing

Layer / File(s) Summary
Responses message_start construction
core/providers/anthropic/responses.go
The Responses converter creates a valid message_start payload without requiring a non-nil response. Available usage, ID, model, and diagnostics remain conditional.
Chat stream message_start framing
core/providers/anthropic/chat.go
The Chat Completions converter initializes usage and content, handles missing messages, preserves roles, and places usage according to the event type.
Serialized framing regression coverage
core/providers/anthropic/messagestartusage_test.go, core/changelog.md
Tests validate serialized message_start frames for Responses and Chat Completions converters. The changelog records the related fixes.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Suggested reviewers: tejasghatte, pratham-mishra04, sammaji

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR fixes the Responses and Chat converters, but the linked issue also identifies an unfixed Bedrock invoke-path message_start usage problem. Update the Bedrock invoke streaming path to always emit a schema-valid message_start usage object, or document why that path is outside this issue’s scope.
✅ Passed checks (4 passed)
Check name Status Explanation
Out of Scope Changes check ✅ Passed The code, tests, and changelog changes directly support the message_start framing fix and linked issue objectives.
Docstring Coverage ✅ Passed Docstring coverage is 85.71% which is sufficient. The required threshold is 80.00%.
Title check ✅ Passed The title clearly identifies the main change: fixing the Anthropic message-start frame.
Description check ✅ Passed The description includes the required sections, explains the fix, identifies affected areas, and provides tests, issue context, and checklist status.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 08-06-fixed_message_start_frame

Comment @coderabbitai help to get the list of available commands.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 6, 2026
@akshaydeo
akshaydeo force-pushed the 08-06-mcp_tool_call_with_error_handling branch from 413be75 to 266a3c4 Compare August 6, 2026 17:18
@akshaydeo
akshaydeo force-pushed the 08-06-fixed_message_start_frame branch from 9dae7ab to 064bc48 Compare August 6, 2026 17:18
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@akshaydeo akshaydeo mentioned this pull request Aug 6, 2026
18 tasks
@akshaydeo
akshaydeo force-pushed the 08-06-mcp_tool_call_with_error_handling branch from 266a3c4 to a8ee2d2 Compare August 6, 2026 18:01
@akshaydeo
akshaydeo force-pushed the 08-06-fixed_message_start_frame branch from 064bc48 to 598cabc Compare August 6, 2026 18:01
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@akshaydeo
akshaydeo force-pushed the 08-06-mcp_tool_call_with_error_handling branch from a8ee2d2 to c8bbad0 Compare August 6, 2026 19:38
@akshaydeo
akshaydeo force-pushed the 08-06-fixed_message_start_frame branch from 598cabc to 64542fe Compare August 6, 2026 19:38
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@akshaydeo
akshaydeo force-pushed the 08-06-fixed_message_start_frame branch from 64542fe to a8c74a6 Compare August 6, 2026 19:57
@akshaydeo
akshaydeo force-pushed the 08-06-mcp_tool_call_with_error_handling branch from c8bbad0 to 0e91527 Compare August 6, 2026 19:57
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

akshaydeo commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

Merge activity

  • Aug 6, 7:58 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Aug 6, 8:07 PM UTC: Graphite rebased this pull request as part of a merge.
  • Aug 6, 8:08 PM UTC: @akshaydeo merged this pull request with Graphite.

@akshaydeo
akshaydeo changed the base branch from 08-06-mcp_tool_call_with_error_handling to graphite-base/5907 August 6, 2026 20:05
@akshaydeo
akshaydeo changed the base branch from graphite-base/5907 to dev August 6, 2026 20:06
@akshaydeo
akshaydeo dismissed coderabbitai[bot]’s stale review August 6, 2026 20:06

The base branch was changed.

@akshaydeo
akshaydeo requested a review from a team as a code owner August 6, 2026 20:06
@akshaydeo
akshaydeo force-pushed the 08-06-fixed_message_start_frame branch from a8c74a6 to ddc2a3c Compare August 6, 2026 20:06
@akshaydeo
akshaydeo merged commit 9303c56 into dev Aug 6, 2026
10 checks passed
@akshaydeo
akshaydeo deleted the 08-06-fixed_message_start_frame branch August 6, 2026 20:08
akshaydeo added a commit that referenced this pull request Aug 7, 2026
## Summary

Strict Anthropic-dialect clients (e.g. `@ai-sdk/anthropic`) validate the very first SSE frame and abort the stream if `message_start` is missing a `message` object, `message.usage`, or `message.content`. This was causing streams to fail when Bifrost served requests backed by Bedrock Converse or any provider routed through `SendCreatedEventResponsesChunk`, which have no usage figures at `message_start` time. Additionally, the Chat Completions → Anthropic converter could panic on a content-less assistant message.

## Changes

- **Responses converter (`responses.go`):** The `message` object is now built unconditionally on `message_start`, even when the upstream `created` event carries no `BifrostResponsesResponse` payload. Usage defaults to all-zeros (neutral under both additive and max-merge accounting) when real counts are not yet available. Nil guards were added for `Response.ID`, `Response.Usage`, and `Response.Diagnostics`.
- **Chat converter (`chat.go`):** `message_start` frames now always include an empty `Usage` struct and an initialized (non-nil) `content` array. The role is read defensively from the message when present, falling back to `assistant`. The model is only attached to the `message` object when one already exists (i.e. on `message_start`), preventing stub objects from being emitted on delta frames. Usage is placed under `message.usage` on `message_start` and at the top level on `message_delta`, matching the Anthropic wire protocol.
- **Tests (`messagestartusage_test.go`):** Consolidated and extended into a shared `requireValidMessageStart` helper that asserts the marshalled wire shape (type, message object, usage object with both token fields, content array) for both the Responses and Chat Completions converters. Added a case for a `created` event with no `Response` payload at all.

## Type of change

- [x] Bug fix

## Affected areas

- [x] Core (Go)
- [x] Providers/Integrations

## How to test

```sh
go test ./core/providers/anthropic/... -run TestResponsesMessageStartAlwaysCarriesUsage
go test ./core/providers/anthropic/... -run TestChatMessageStartAlwaysCarriesUsage
go test ./...
```

Send a streaming `/v1/messages` request routed through a Bedrock Converse model using an `@ai-sdk/anthropic` client and confirm the stream completes without a `Type validation failed ... path: [message, usage]` error on the first frame.

## Breaking changes

- [x] No

## Related issues

Closes #5885

## Security considerations

None.

## Checklist

- [x] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [x] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [x] I verified the CI pipeline passes locally if applicable
atharvamhaske pushed a commit to atharvamhaske/bifrost that referenced this pull request Aug 13, 2026
## Summary

Strict Anthropic-dialect clients (e.g. `@ai-sdk/anthropic`) validate the very first SSE frame and abort the stream if `message_start` is missing a `message` object, `message.usage`, or `message.content`. This was causing streams to fail when Bifrost served requests backed by Bedrock Converse or any provider routed through `SendCreatedEventResponsesChunk`, which have no usage figures at `message_start` time. Additionally, the Chat Completions → Anthropic converter could panic on a content-less assistant message.

## Changes

- **Responses converter (`responses.go`):** The `message` object is now built unconditionally on `message_start`, even when the upstream `created` event carries no `BifrostResponsesResponse` payload. Usage defaults to all-zeros (neutral under both additive and max-merge accounting) when real counts are not yet available. Nil guards were added for `Response.ID`, `Response.Usage`, and `Response.Diagnostics`.
- **Chat converter (`chat.go`):** `message_start` frames now always include an empty `Usage` struct and an initialized (non-nil) `content` array. The role is read defensively from the message when present, falling back to `assistant`. The model is only attached to the `message` object when one already exists (i.e. on `message_start`), preventing stub objects from being emitted on delta frames. Usage is placed under `message.usage` on `message_start` and at the top level on `message_delta`, matching the Anthropic wire protocol.
- **Tests (`messagestartusage_test.go`):** Consolidated and extended into a shared `requireValidMessageStart` helper that asserts the marshalled wire shape (type, message object, usage object with both token fields, content array) for both the Responses and Chat Completions converters. Added a case for a `created` event with no `Response` payload at all.

## Type of change

- [x] Bug fix

## Affected areas

- [x] Core (Go)
- [x] Providers/Integrations

## How to test

```sh
go test ./core/providers/anthropic/... -run TestResponsesMessageStartAlwaysCarriesUsage
go test ./core/providers/anthropic/... -run TestChatMessageStartAlwaysCarriesUsage
go test ./...
```

Send a streaming `/v1/messages` request routed through a Bedrock Converse model using an `@ai-sdk/anthropic` client and confirm the stream completes without a `Type validation failed ... path: [message, usage]` error on the first frame.

## Breaking changes

- [x] No

## Related issues

Closes maximhq#5885

## Security considerations

None.

## Checklist

- [x] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [x] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [x] I verified the CI pipeline passes locally if applicable
akshaydeo pushed a commit that referenced this pull request Aug 20, 2026
## Summary

Adds E2E regression test coverage for issue #5885, where strict Anthropic-dialect clients (`@ai-sdk/anthropic`, the official Anthropic Python SDK, and Claude Code subagents) crash with `"undefined is not an object (evaluating 'o.input_tokens')"` when `message_start.message.usage` is absent from the first SSE frame. The fix (PR #5907, `db388c35c`) ensured a zero-value usage placeholder is always serialized instead of being silently dropped by Go's `omitempty` JSON tag.

## Changes

- Adds test group **55** to the provider harness collection covering three distinct paths that previously omitted `message_start.message.usage`:
  - **Case 1** — OpenAI Responses API path (`openai/gpt-4o-mini` → `/anthropic/v1/messages` streaming), the exact route reported in #5885
  - **Case 2** — xAI Responses-API-compatible path (`xai/grok-4-0709` → `/anthropic/v1/messages` streaming)
  - **Case 3** — Bedrock Converse `invoke-with-response-stream` path (`claude-haiku-4-5`), the route that broke Claude Code's Bedrock-backed subagents
- Each test asserts that the `message_start` frame contains a `usage` object with numeric `input_tokens` and `output_tokens` fields, not merely that the stream completes successfully.

## Type of change

- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [ ] Core (Go)
- [ ] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

Run the provider harness E2E collection against a live Bifrost instance and confirm test group 55 passes for all three cases:

```sh
# Ensure BIFROST_BASE_URL / baseUrl is pointed at a running Bifrost instance
# with OpenAI, xAI, and Bedrock credentials configured, then execute the
# Postman/Newman collection:
newman run tests/e2e/api/collections/provider-harness.json \
  --env-var baseUrl=<your-bifrost-base-url>
```

Expected outcome: all three tests in group 55 report `message_start.message.usage` as an object with numeric `input_tokens` and `output_tokens`.

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

Closes #5885 (regression coverage); references PR #5907 (`db388c35c`).

## Security considerations

None — test-only change with no auth, secrets, or PII implications.

## Checklist

- [x] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [x] I verified the CI pipeline passes locally if applicable
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]: v1.6.8 omits message_start.message.usage on Bedrock-backed providers, breaking @ai-sdk/anthropic streaming

1 participant