Skip to content

🧵 fix: Harmony Headers in Text Attachments Break OpenAI Models on Bedrock - #17101

Merged
danny-avila merged 4 commits into
devfrom
danny-avila/bedrock-openai-text-files
Oct 16, 2026
Merged

danny-avila merged 4 commits into
devfrom
danny-avila/bedrock-openai-text-files

Conversation

@danny-avila

Copy link
Copy Markdown
Collaborator

Pull Request

Summary

Agents running an OpenAI model on Bedrock (global.openai.gpt-6.1-sol) fail with An error occurred while processing the request: Cannot use 'in' operator to search for 'tool_calls' in undefined once the conversation holds a text attachment whose content includes an OpenAI harmony header such as <|channel|>analysis (a pasted model transcript, or notes on the format). LibreChat sends text/plain attachments inline as file context, so every later turn resends the text. Bedrock answers such a request with 200 OK and an event stream that closes before a single event, and @librechat/agents 4.0.4 turned the empty stream into { messages: [undefined] } and crashed collecting tool calls. Images carry no text, other Bedrock model families are unaffected, and a fork made with "Branch without files" works.

The fix belongs to the SDK's Bedrock converter, which is the only layer that sees every text the model receives (system prompt, attachments, tool results, fetched pages) and knows the target model: LibreChat-AI/agents#611 neutralizes harmony control tokens for OpenAI models on Bedrock (<|channel|> becomes <|channel|>, which the model still reads), rejects a Bedrock stream that ends without any event with an explanatory error, and reports a missing attempt message as an empty response instead of a TypeError.

This PR adds the LibreChat side of the regression coverage: a spec that drives LibreChat's own file-context formatting (extractFileContext, prependFileContext, formatAgentMessages) through the SDK's Bedrock model with a recorded OpenAI-on-Bedrock response, and asserts the header reaches an OpenAI model neutralized and other model families unchanged. It fails on 4.0.4 and passes against the SDK branch; the @librechat/agents bump lands here once the release containing LibreChat-AI/agents#611 is published.

Depends on LibreChat-AI/agents#611

How it works

Production stack, reproduced in the demo pod with 4.0.4, real Bedrock and a synthetic attachment:

TypeError: Cannot use 'in' operator to search for 'tool_calls' in undefined
    at .../@librechat/agents/dist/cjs/llm/invoke.cjs:269:68
    at Array.flatMap (<anonymous>)
    at attemptInvoke (.../@librechat/agents/dist/cjs/llm/invoke.cjs:269:34)

Request path for the attachment:

extractFileContext        # Attached document(s): ```md # "pasted-text.txt" …<|channel|>analysis…
  prependFileContext      # inline into the user turn, current and historical
    formatAgentMessages
      CustomChatBedrockConverse._streamResponseChunks
        convertToConverseMessages   # agents#611: <|name|> → <|name|> for openai.* models
        ConverseStream              # unpatched: 200 OK, zero events

Type of change

  • Bug fix
  • Tests / tooling / CI

Testing

Live characterization against global.openai.gpt-6.1-sol (us-west-2, synthetic text): any user text containing <|channel|>analysis (also with spaces or zero-width characters inside the token) returns zero events on every run; <|channel|>final, <|start|>user…, single tokens, and the fullwidth-bar form respond normally. A Bedrock document block for this model is rejected outright (This model doesn't support documents.), so text attachments only reach it as file context. With the SDK branch, the same attached-file request completes 3/3 through attemptInvoke against real Bedrock.

Tested environments/configuration:

  • Provider/model: Bedrock global.openai.gpt-6.1-sol, us-west-2

Automated tests:

Screenshots / recordings

No user-facing change

Risk / compatibility

Text sent to OpenAI models on Bedrock shows harmony tokens with fullwidth bars; the model still reads them (asked which channel a neutralized example uses, it answers analysis). Other providers and Bedrock model families are untouched.

Until the bump ships, a conversation that already holds such an attachment can continue via "Branch without files", or by switching the agent to a non-OpenAI Bedrock model.

Checklist

  • I reviewed my own changes
  • Relevant tests have been added or updated
  • Existing relevant tests pass
  • The change does not introduce new warnings or errors
  • User-facing or complex behavior is documented where necessary
  • Required dependency changes have been merged/published
  • Required documentation PR: N/A

…ls on Bedrock

A pasted text/plain attachment is sent inline as file context, so a harmony header such as `<|channel|>analysis` inside it reaches every later turn. OpenAI models on Bedrock answer that request with an event stream that closes before any event, which crashed the turn in @librechat/agents 4.0.4 with `Cannot use 'in' operator to search for 'tool_calls' in undefined`.

Drive LibreChat's own file-context formatting through the SDK's Bedrock model with a recorded response and assert the header reaches an OpenAI model neutralized and other model families unchanged.
@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

Pass 1 of 3 on head e18eb11.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-16T11:18:43.491870Z 484bafc Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e18eb11c49

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +92 to +93
expect(text).toContain(`# "pasted-text.txt"\nNotes on the format:\n${NEUTRALIZED}`);
expect(text).not.toContain('<|');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Bump the SDK before enabling this regression test

The checked-in package-lock.json still installs @librechat/agents 4.0.4, while the neutralization asserted here is only implemented by the unreleased agents#611 change. Consequently, every locked install running the normal packages/api Jest suite sends the original HEADER and fails this assertion; production also remains unfixed. Update packages/api/package.json and the lockfile to an SDK release containing that change before adding this test.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, and intended: this PR stays a draft until the @librechat/agents release containing LibreChat-AI/agents#611 is published, and the packages/api/api dependency and lockfile bump lands here before it is marked ready. The spec is checked in first so it demonstrates the failure on 4.0.4 (it fails with the raw header) and passes against the SDK branch build.

@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

Pass 2 of 3 on head e18eb11.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Bravo.

Reviewed commit: e18eb11c49

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

This is the final review for this PR (pass 3 of 3) of head e18eb11. Please flag only blocking issues: correctness, security, or data loss.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Nice work!

Reviewed commit: e18eb11c49

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

This is the final review for this PR (pass 4 of 4) of head 484bafc. Please flag only blocking issues: correctness, security, or data loss.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Breezy!

Reviewed commit: 484bafcd0e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@danny-avila
danny-avila marked this pull request as ready for review October 16, 2026 13:51
@danny-avila
danny-avila merged commit b760839 into dev Oct 16, 2026
49 checks passed
@danny-avila
danny-avila deleted the danny-avila/bedrock-openai-text-files branch October 16, 2026 22:00
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