fix(ai-proxy): map upstream LLM timeouts to 504 instead of 500#13481
Merged
Conversation
handle_error() only matched the contiguous substring "timeout", so OS-level
connect timeouts (ETIMEDOUT -> "connection timed out" / "operation timed out")
and DNS resolver timeouts ("Operation timed out") fell through to 500. These
are gateway-timeout conditions and should surface as 504. Match "timed out"
in addition to "timeout" so every timeout variant maps to 504, while
non-timeout errors keep returning 500.
There was a problem hiding this comment.
Pull request overview
This PR fixes AI upstream timeout handling by mapping additional upstream timeout error strings (notably "... timed out" variants coming from OS errno / resolver) to 504 Gateway Timeout instead of incorrectly returning 500 Internal Server Error.
Changes:
- Extend
apisix.plugins.ai-transport.http.handle_errorto match bothtimeoutandtimed out. - Add regression and matrix tests to ensure all known timeout spellings map to
504, while non-timeout network errors remain500.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
apisix/plugins/ai-transport/http.lua |
Expands timeout detection logic in handle_error to cover "... timed out" errors and documents the rationale. |
t/plugin/ai-transport-http.t |
Adds tests covering Operation timed out and a representative matrix of timeout/non-timeout strings for handle_error. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
AlinsRan
approved these changes
Jun 9, 2026
membphis
approved these changes
Jun 9, 2026
shreemaan-abhishek
approved these changes
Jun 9, 2026
wistefan
pushed a commit
to wistefan/apisix
that referenced
this pull request
Jun 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
When an AI proxy request times out reaching the upstream LLM, the client receives
500 Internal Server Errorinstead of504 Gateway Timeout.apisix/plugins/ai-transport/http.luamaps errors to504only when the error string contains the contiguous substringtimeout:core.string.findis a plain-text search, and an OS connect timeout surfaces asOperation timed out/Connection timed out, which containstimed out(with a space), not the contiguoustimeout. So it falls through to the default500.Timeout error taxonomy
handle_errorreceives its error fromlua-resty-httpover OpenResty cosockets. lua-resty-http propagates the raw cosocket error unchanged (return nil, errin every connect/send/receive/body-reader path), so the only timeout spellings that can reachhandle_errorare produced by the cosocket layer and the nginx resolver:handle_errorset_timeoutvalue fires)ngx_http_lua_socket_tcp.c,FT_TIMEOUT)timeouttimeoutETIMEDOUT, Linuxconnection timed outtimed outETIMEDOUT, macOS/BSDoperation timed outtimed outngx_resolver_strerror(hardcoded)Operation timed outtimed outThe complete timeout set collapses to exactly two substrings:
timeout(cosocket timer) andtimed out(errno / resolver). Matching both is necessary (the baretimeoutcase has no space; the… timed outcases have no contiguoustimeout) and sufficient. Non-timeout errors (connection refused,connection reset by peer,closed) keep returning500.Fix
Match
timed outin addition totimeout:handle_erroris the single status mapper behind all AI upstream failure paths (ai-proxy request/metric, ai-providers sidecar request and streaming read), so all of them now return504on timeout.Tests
Added two cases to
t/plugin/ai-transport-http.t:connectreturningOperation timed outand asserts the mapped status is504;Both fail before the change (the
… timed outcases return500) and pass after.Checklist