Skip to content

fix(ai-proxy): return 502 when streaming converter receives mismatched response format#13229

Merged
nic-6443 merged 4 commits into
apache:masterfrom
nic-6443:fix/streaming-converter-502
Apr 16, 2026
Merged

fix(ai-proxy): return 502 when streaming converter receives mismatched response format#13229
nic-6443 merged 4 commits into
apache:masterfrom
nic-6443:fix/streaming-converter-502

Conversation

@nic-6443
Copy link
Copy Markdown
Member

When a protocol converter is active (e.g., Anthropic-to-OpenAI) and the upstream returns SSE events in an unexpected format that the target protocol parser cannot parse, the gateway crashes with a 500 error in the balancer phase.

Root cause: if the target protocol SSE parser skips all upstream events (unrecognized format), no output is ever sent to the client via ngx.print(). The response never gets committed, so nginx falls through to proxy_pass → balancer phase, which crashes on nil upstream_conf.

Fix: track whether any output was produced during converter-mode streaming in parse_streaming_response. If the entire stream is consumed without producing any output, return 502 (Bad Gateway) with a descriptive error instead of letting nginx fall through to the balancer.

This is scoped to converter mode only — passthrough streaming is not affected.

…d response format

When a protocol converter is active (e.g., Anthropic-to-OpenAI) and the
upstream returns SSE events in an unexpected format that the target protocol
parser cannot parse, the gateway would crash with a 500 error in the balancer
phase because no output was ever sent to the client.

This happens because:
1. The target protocol's SSE parser skips all unrecognized events
2. The converter never receives any parsed events to convert
3. No ngx.print() is called, so the response is never committed
4. nginx falls through to proxy_pass/balancer with nil upstream_conf

Fix: Track whether any output was produced during converter-mode streaming.
If the stream ends without any output, return 502 (Bad Gateway) with a
descriptive error message instead of letting nginx fall through to the
balancer.
Copilot AI review requested due to automatic review settings April 16, 2026 03:33
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. bug Something isn't working labels Apr 16, 2026
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens ai-proxy streaming behavior in protocol-converter mode so that when the upstream SSE stream is in an unexpected format (and therefore produces no convertible output), APISIX returns 502 instead of leaving the response uncommitted and falling through into nginx’s balancer phase (leading to a crash/500).

Changes:

  • Track whether any converted streaming output was emitted; if the stream ends with no output in converter mode, return 502 and log an error.
  • Handle EOF buffered SSE remainder by converting and emitting any remaining events in converter mode.
  • Add a regression test covering the mismatched upstream SSE format scenario.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
apisix/plugins/ai-providers/base.lua Adds “no converted output” detection in converter-mode streaming and returns 502 to prevent nginx balancer fallthrough/crash.
t/plugin/ai-proxy-protocol-conversion.t Adds a streaming regression test for mismatched upstream SSE format when conversion is active.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread t/plugin/ai-proxy-protocol-conversion.t Outdated
Comment thread apisix/plugins/ai-providers/base.lua Outdated
Comment thread t/plugin/ai-proxy-protocol-conversion.t Outdated
- Return descriptive error message as second return value from both 502 paths
- Fix test: remove ngx.status propagation so outer response stays 200
The EOF remainder only contains incomplete SSE data that won't decode
to valid events, so converter processing there is dead code. Simplified
to just check output_sent after the existing usage extraction loop.

Also remove unused variable in test.
The previous EOF remainder code tried to sse.decode() an incomplete SSE
buffer, which can never produce valid events. Replace with a simple
warning log.
@nic-6443 nic-6443 merged commit da45cbe into apache:master Apr 16, 2026
19 checks passed
@nic-6443 nic-6443 deleted the fix/streaming-converter-502 branch April 16, 2026 07:52
@nic-6443 nic-6443 self-assigned this Apr 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants