bug: ai-prompt-guard returns an empty 200 instead of proxying when no content is selected for checking
- Dominant language
- Lua
- Stars
- 17.1k
- Forks
- 2.9k
- Avg merge
- 3d 16h
- Merged PRs (30d)
- 63
Description
Raised on #13731 at the maintainers' request, split out from that docs PR.
### Current Behavior
When the set of messages selected for checking is empty, `ai-prompt-guard` returns `200` from `_M.access` (`apisix/plugins/ai-prompt-guard.lua:162-164`). `plugin.lua:1406-1419` hands any non-nil code to `core.response.exit`, and `core/response.lua:126-134` calls `ngx_exit(code)` for any code — the `>= 400` branch only adds tracing. The client gets an empty 200 and the upstream is never called.
Two ordinary requests reach it:
- `match_all_roles` left at its default `false` (`ai-prompt-guard.lua:38-41`) and no `user` message in the request, e.g. only a `system` message. Non-user messages are filtered out (`:152-154`), the list is empty, and the request is answered with an empty 200. No non-default configuration is needed to reach this.
- Embeddings with `input` as an array of strings. `ai-protocols/openai-embeddings.lua:109-117` only collects a string `input`, so an array yields `{}`.
The repo already uses both meanings of the return value: `cors.lua:338-339` returns `200` deliberately to end an OPTIONS preflight, and `ai-lakera-guard.lua:104-105` uses a bare `return` for the same "no messages" condition and lets the request continue.
Nothing is logged: the warn at `plugin.lua:1408-1409` only fires for codes `>= 400`.
### Expected Behavior
Nothing to check should mean pass through to the upstream, as `ai-lakera-guard` does. The fix is `return` instead of `return 200`.
The tests don't catch this. `t/plugin/ai-prompt-guard.t` TEST 9 (line 243, "send request with bad word and it will pass for non user") is exactly the first case — its route, from TEST 8, sets only `allow_patterns` and leaves `match_all_roles` at the default — and it asserts neither `--- response_body` nor `--- error_code`, so it passes whether the request was proxied or cut off. TESTs 3, 6, 14, 20, 23 and 26 have the same gap. Adding `--- response_body` / `hello world` (`t/lib/server.lua:74-79`) would make them discriminate, following `t/plugin/ai-lakera-guard.t`, which already asserts `--- error_code: 200` with `--- response_body_like` on its allow paths.
### Error Logs
None — see the note on logging above.
### Steps to Reproduce
1. Use the route from TEST 8 in `t/plugin/ai-prompt-guard.t` (only `allow_patterns` set; `match_all_roles` at its default).
2. Add `--- response_body` with `hello world` to TEST 9.
3. Run `prove t/plugin/ai-prompt-guard.t` — TEST 9 should fail on current `master`, and pass once `return 200` becomes `return`.
### Environment
`master` at `d13105e17` (2026-09-14). Traced from source, not run — I don't have an APISIX test environment set up, so treat the reproduction above as the expected result rather than an observed one.
Happy to open the PR with the one-line fix and the test assertions.
Contributor guide
Research direction
Start with the return path in apisix/plugins/ai-prompt-guard.lua and compare the no-messages behavior with ai-lakera-guard.lua, then inspect TEST 9 and the related cases in t/plugin/ai-prompt-guard.t. Run `prove t/plugin/ai-prompt-guard.t`; done means empty selections reach the upstream and the tests assert the `hello world` response.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- lua, nginx
- Domain
- api, testing
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 88/100