This is an automated email from the ASF dual-hosted git repository.
nic-6443 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git
The following commit(s) were added to refs/heads/master by this push:
new e99fb63fe fix(ai-proxy): map upstream LLM timeouts to 504 instead of
500 (#13481)
e99fb63fe is described below
commit e99fb63feda3a12320d29cdba4dbfe089d9eea38
Author: Nic <[email protected]>
AuthorDate: Tue Jun 9 14:04:55 2026 +0800
fix(ai-proxy): map upstream LLM timeouts to 504 instead of 500 (#13481)
---
apisix/plugins/ai-transport/http.lua | 4 ++-
t/plugin/ai-transport-http.t | 65 ++++++++++++++++++++++++++++++++++++
2 files changed, 68 insertions(+), 1 deletion(-)
diff --git a/apisix/plugins/ai-transport/http.lua
b/apisix/plugins/ai-transport/http.lua
index d92ecf5ba..eb7efc34b 100644
--- a/apisix/plugins/ai-transport/http.lua
+++ b/apisix/plugins/ai-transport/http.lua
@@ -36,8 +36,10 @@ local rapidjson_null = rapidjson.null
--- Map network errors to HTTP status codes.
+-- Cosocket timers report "timeout"; OS errno (ETIMEDOUT) and the resolver
+-- report "... timed out", so both spellings must be matched.
function _M.handle_error(err)
- if core.string.find(err, "timeout") then
+ if core.string.find(err, "timeout") or core.string.find(err, "timed out")
then
return 504
end
return 500
diff --git a/t/plugin/ai-transport-http.t b/t/plugin/ai-transport-http.t
index a2cb27883..b47516493 100644
--- a/t/plugin/ai-transport-http.t
+++ b/t/plugin/ai-transport-http.t
@@ -288,3 +288,68 @@ failed to encode AI request body with rapidjson:
{"messages":[{"content":"hi","role":"user"},{"content":"hello","role":"assistant"}],"model":"m"}
--- no_error_log
failed to encode AI request body with rapidjson:
+
+
+
+=== TEST 6: connect timeout ("Operation timed out") maps to 504
+--- config
+ location /t {
+ content_by_lua_block {
+ local orig_http = package.loaded["resty.http"]
+ local orig_transport =
package.loaded["apisix.plugins.ai-transport.http"]
+
+ package.loaded["resty.http"] = {
+ new = function()
+ return {
+ set_timeout = function() end,
+ connect = function() return nil, "Operation timed out"
end,
+ }
+ end,
+ }
+
+ package.loaded["apisix.plugins.ai-transport.http"] = nil
+ local transport = require("apisix.plugins.ai-transport.http")
+ local res, err = transport.request({
+ host = "127.0.0.1",
+ port = 80,
+ path = "/",
+ body = {model = "m"},
+ }, 1000)
+ ngx.say("err: ", err)
+ ngx.say("status: ", transport.handle_error(err))
+
+ package.loaded["resty.http"] = orig_http
+ package.loaded["apisix.plugins.ai-transport.http"] = orig_transport
+ }
+ }
+--- response_body
+err: connect: Operation timed out
+status: 504
+
+
+
+=== TEST 7: handle_error maps every timeout spelling to 504, others to 500
+--- config
+ location /t {
+ content_by_lua_block {
+ local transport = require("apisix.plugins.ai-transport.http")
+ local cases = {
+ "connect: timeout",
+ "connect: connection timed out",
+ "connect: operation timed out",
+ "connect: Operation timed out",
+ "request: connection refused",
+ "request: connection reset by peer",
+ }
+ for _, e in ipairs(cases) do
+ ngx.say(e, " => ", transport.handle_error(e))
+ end
+ }
+ }
+--- response_body
+connect: timeout => 504
+connect: connection timed out => 504
+connect: operation timed out => 504
+connect: Operation timed out => 504
+request: connection refused => 500
+request: connection reset by peer => 500