Current Behavior
After upgrading Apache APISIX from 3.10 to 3.15, we started seeing errors from the workflow plugin during the log phase:
failed to run log_by_lua*: /usr/local/apisix/apisix/plugins/workflow.lua:182: attempt to index field '_workflow_cache' (a nil value)
stack traceback:
/usr/local/apisix/apisix/plugins/workflow.lua:182: in function 'phase_func'
/usr/local/apisix/apisix/plugin.lua:1231: in function 'run_plugin'
/usr/local/apisix/apisix/plugin.lua:1331: in function 'run_global_rules'
/usr/local/apisix/apisix/init.lua:472: in function 'common_phase'
/usr/local/apisix/apisix/init.lua:1064: in function 'http_log_phase'
The affected requests are mainly OPTIONS preflight requests.
Expected Behavior
The workflow plugin should not throw an error in the log phase when ctx._workflow_cache is not initialized.
Error Analysis
In APISIX 3.15, workflow.lua added a log phase handler introduced by:
The current logic initializes ctx._workflow_cache only in the access phase:
function _M.access(conf, ctx)
ctx._workflow_cache = ctx._workflow_cache or {}
...
ctx._workflow_cache[idx] = match_result
end
However, the log phase assumes the field always exists:
function _M.log(conf, ctx)
for idx, rule in ipairs(conf.rules) do
local match_result = ctx._workflow_cache[idx]
...
end
end
If a request is finalized before workflow.access() runs, for example an OPTIONS request handled earlier by CORS or another plugin, the log phase can still execute while ctx._workflow_cache is nil.
Possible Fix
Add a nil guard at the beginning of workflow.log():
function _M.log(conf, ctx)
if not ctx._workflow_cache then
return
end
for idx, rule in ipairs(conf.rules) do
local match_result = ctx._workflow_cache[idx]
...
end
end
Environment
- APISIX version: 3.15
- Previous version without this issue: 3.10
- Plugin involved: workflow
- Request type: mainly OPTIONS preflight requests
Expected Behavior
No response
Error Logs
No response
Steps to Reproduce
-
Start APISIX 3.15 with the workflow, cors, and limit-conn plugins enabled.
-
Configure a global rule that uses the workflow plugin with a limit-conn action.
Example:
{
"plugins": {
"workflow": {
"rules": [
{
"case": [
["http_method", "==", "GET"]
],
"actions": [
[
"limit-conn",
{
"conn": 1,
"burst": 0,
"default_conn_delay": 0,
"key": "remote_addr",
"rejected_code": 503
}
]
]
}
]
}
}
}
- Configure a route with the cors plugin enabled so that OPTIONS preflight requests are handled before the access phase.
Example:
{
"uri": "/test",
"plugins": {
"cors": {}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}
- Send an OPTIONS preflight request:
curl -i -X OPTIONS "http://127.0.0.1:9080/test" \
-H "Origin: https://example.com" \
-H "Access-Control-Request-Method: GET"
- Check the APISIX error log.
Actual result:
failed to run log_by_lua*: /usr/local/apisix/apisix/plugins/workflow.lua:182: attempt to index field '_workflow_cache' (a nil value)
Expected result:
APISIX should handle the request without logging an error. The workflow plugin should skip the log phase safely when ctx._workflow_cache was not initialized in the access phase.
The core trigger is: workflow.log() runs, but workflow.access() did not run for that request.
Environment
- APISIX version (run
apisix version):
- Operating system (run
uname -a):
- OpenResty / Nginx version (run
openresty -V or nginx -V):
- etcd version, if relevant (run
curl http://127.0.0.1:9090/v1/server_info):
- APISIX Dashboard version, if relevant:
- Plugin runner version, for issues related to plugin runners:
- LuaRocks version, for installation issues (run
luarocks --version):
Current Behavior
After upgrading Apache APISIX from 3.10 to 3.15, we started seeing errors from the
workflowplugin during the log phase:The affected requests are mainly OPTIONS preflight requests.
Expected Behavior
The workflow plugin should not throw an error in the log phase when ctx._workflow_cache is not initialized.
Error Analysis
In APISIX 3.15, workflow.lua added a log phase handler introduced by:
The current logic initializes ctx._workflow_cache only in the access phase:
However, the log phase assumes the field always exists:
If a request is finalized before workflow.access() runs, for example an OPTIONS request handled earlier by CORS or another plugin, the log phase can still execute while ctx._workflow_cache is nil.
Possible Fix
Add a nil guard at the beginning of workflow.log():
Environment
Expected Behavior
No response
Error Logs
No response
Steps to Reproduce
Start APISIX 3.15 with the
workflow,cors, andlimit-connplugins enabled.Configure a global rule that uses the
workflowplugin with alimit-connaction.Example:
{ "plugins": { "workflow": { "rules": [ { "case": [ ["http_method", "==", "GET"] ], "actions": [ [ "limit-conn", { "conn": 1, "burst": 0, "default_conn_delay": 0, "key": "remote_addr", "rejected_code": 503 } ] ] } ] } } }Example:
Actual result:
failed to run log_by_lua*: /usr/local/apisix/apisix/plugins/workflow.lua:182: attempt to index field '_workflow_cache' (a nil value)
Expected result:
APISIX should handle the request without logging an error. The workflow plugin should skip the log phase safely when ctx._workflow_cache was not initialized in the access phase.
The core trigger is:
workflow.log()runs, butworkflow.access()did not run for that request.Environment
apisix version):uname -a):openresty -Vornginx -V):curl http://127.0.0.1:9090/v1/server_info):luarocks --version):