-
Type:
Problem report
-
Resolution: Unresolved
-
Priority:
Trivial
-
None
-
Affects Version/s: 7.4.13, 7.4.14
-
Component/s: Server (S)
-
None
-
Environment:Official Docker images zabbix/zabbix-server-pgsql:7.4.13-alpine and :7.4.14-alpine, PostgreSQL 18 backend, running under Kubernetes.
Creating a web scenario through httptest.create without specifying posts in the step produces a step whose posts field is stored as the string "0" (with post_type = 0), where an empty raw POST body should be an empty string.
On the scenario's first execution the http poller child process receives SIGSEGV. The parent then shuts down the whole server cleanly. Under a supervisor the server restarts and crashes again 10-70 s later, i.e. it crash-loops for as long as the scenario exists. Deleting the scenario restores normal operation immediately.
The frontend appears to always send posts, so this is only reachable through the API. In our instance 51 pre-existing scenarios ran for years without incident; the first API-created scenario crash-looped the server for ~30 minutes.
Confirmed on 7.4.13 (revision 3c95000) and 7.4.14 (revision 9e85f21).
Steps to reproduce
Against any reachable HTTPS or HTTP URL, on a working server:
curl -s -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json-rpc" \ -d '{ "jsonrpc": "2.0", "method": "httptest.create", "params": { "name": "crash repro", "hostid": "<any monitored hostid>", "delay": "1m", "steps": [ { "name": "step1", "no": 1, "url": "https://example.com/", "status_codes": "200" } ] }, "id": 1 }' https://<zabbix>/api_jsonrpc.php
Note there is no posts key in the step. Then wait for the scenario's first execution (up to delay).
httptest.get with selectSteps: "extend" then shows:
post_type = "0" posts = "0" <-- string "0", not ""
Expected result
The scenario executes. An omitted posts should mean "no POST body" and be stored as an empty string, as it is when posts: "" is passed explicitly.
Actual result
7.4.14:
1:20260904:132052.051 Starting Zabbix Server. Zabbix 7.4.14 (revision 9e85f21).
51:20260904:132057.449 server #15 started [http poller #3]
51:20260904:133053.776 Got signal [signal:11(SIGSEGV),reason:1,refaddr:0]. Crashing ...
1:20260904:133054.898 One child process died (PID:51,exitcode/signal:1). Exiting ...
1:20260904:133059.796 Zabbix Server stopped. Zabbix 7.4.14 (revision 9e85f21).
7.4.13, same but a different poller instance:
49:20260904:115452.898 Got signal [signal:11(SIGSEGV),reason:1,refaddr:0]. Crashing ...
1:20260904:115453.220 One child process died (PID:49,exitcode/signal:1). Exiting ...
The crashing PID is an http poller in every case (#2 on one run, #3 on another), so it is the poller role rather than a particular worker.
Isolation
Each row is a separate scenario created via httptest.create on an otherwise idle and stable server, deleted immediately after the outcome was observed.
| post_type | posts | result | |
|---|---|---|---|
| A | "0" | "0" (omitted) | CRASH after 43 s |
| B | "0" | "0" (omitted) | CRASH after 11 s |
| C | "0" | "0" (omitted) | CRASH after 71 s |
| D | "0" | "0" (omitted) | CRASH after 54 s |
| E | "0" | "0" (omitted) | CRASH after 23 s |
| F | "1" | [] (explicit) | survived 260 s |
| G | "0" | "" (explicit) | survived 260 s |
Row G is the key control: identical post_type = 0 raw-POST code path, differing from the crashing rows only in posts being "" instead of "0". It does not crash. So the trigger is the stored value, not the raw POST type.
The following were each tested and are not required for the crash:
- self-signed vs publicly trusted certificate (crashes with a valid, trusted cert)
- non-standard port (crashes on 443)
- macros in the step URL (crashes with a literal URL)
- status_codes set or empty
- verify_peer / verify_host - all four combinations crash, including 1/1
- the specific host - crashes on multiple hosts, with and without linked templates
Control: with no scenario present the same server ran 300 s with zero restarts, so the crashes are caused by the scenario and not by background instability.
Workaround
Always pass posts explicitly when creating web scenarios via the API:
- "posts": "" for a raw (empty) body
- "posts": [] for form data
Both are confirmed safe. Scenarios created through the frontend are unaffected.
Additional notes
- post_type cannot be set through httptest.create in 7.4 - it is rejected with unexpected parameter "post_type" and is inferred from whether posts is a string or an array. So the malformed value cannot be avoided by setting post_type directly, only by supplying posts.
- Because the parent exits cleanly after the child dies, the container exit code is 0 and orchestrators report a successful exit. Under Kubernetes this shows as CrashLoopBackOff with exit code 0, which reads as benign and made this considerably harder to diagnose.
- Secondary effect: while the server is restarting every ~60 s, any check whose interval exceeds the server's uptime never executes, silently and with no alert. Short-interval checks continue to collect, which masks the outage.
- No backtrace: the official Alpine images ship without debug symbols. Happy to provide one if you can point me at a debug build or the preferred way to capture a core dump from the official image.
- This is not
ZBX-27770(7.4.10 web scenario crash) - that is fixed in 7.4.12rc1 and therefore already present in both versions tested here.