Every ping() and every ping_sweep() probe fails against a live OPNsense:
ping job creation failed for 10.30.0.1:
{'result': 'failed', 'validations': {'ping.settings.hostname': 'A value is required.'}}
Found 2026-08-22 through netOrk, where a LAN scan from a firewall reported 254 hosts scanned and 0 alive, with no error at all.
Root cause
_ping_model_node() reads the model from GET /api/diagnostics/ping/get and takes the single dict-valued key as the node to post under. The real response nests two levels:
So the helper returned "ping" and _ping_job_create posted {"ping": {hostname: …}}, putting the fields where OPNsense expects the settings node. The hostname never arrived, and the API said so on every single call.
Why the tests passed
FakePingAPI in tests/unit/test_ping.py answered /get with a one-level model, {node: {"hostname": "", "fam": "ip"}}, and read the posted payload back through the same assumption. The fake agreed with the code about a shape neither shares with the device.
Fix
_ping_model_path() returns the whole chain of single-dict wrappers (("ping", "settings")), stopping at the first level with more than one key — i.e. the field level, where fam is a dict too and descending further would land inside a form field. _wrap_in_model() nests the settings accordingly. Both the two-level and a hypothetical one-level model work, and the default when /get cannot be read is what current firmware ships.
Verified against a live OPNsense: job accepted ({"result": "ok", "uuid": …}), 10.30.0.1 answered 3 of 3 at 0.116 ms.
## Symptom
Every `ping()` and every `ping_sweep()` probe fails against a live OPNsense:
```
ping job creation failed for 10.30.0.1:
{'result': 'failed', 'validations': {'ping.settings.hostname': 'A value is required.'}}
```
Found 2026-08-22 through netOrk, where a LAN scan from a firewall reported 254 hosts scanned and 0 alive, with no error at all.
## Root cause
`_ping_model_node()` reads the model from `GET /api/diagnostics/ping/get` and takes the single dict-valued key as the node to post under. The real response nests **two** levels:
```json
{"ping": {"settings": {"hostname": "", "fam": {"ip": {...}, "ip6": {...}},
"source_address": "", "packetsize": "", "disable_frag": "0",
"interval": "", "description": ""}}}
```
So the helper returned `"ping"` and `_ping_job_create` posted `{"ping": {hostname: …}}`, putting the fields where OPNsense expects the `settings` node. The hostname never arrived, and the API said so on every single call.
## Why the tests passed
`FakePingAPI` in `tests/unit/test_ping.py` answered `/get` with a **one-level** model, `{node: {"hostname": "", "fam": "ip"}}`, and read the posted payload back through the same assumption. The fake agreed with the code about a shape neither shares with the device.
## Fix
`_ping_model_path()` returns the whole chain of single-dict wrappers (`("ping", "settings")`), stopping at the first level with more than one key — i.e. the field level, where `fam` is a dict too and descending further would land inside a form field. `_wrap_in_model()` nests the settings accordingly. Both the two-level and a hypothetical one-level model work, and the default when `/get` cannot be read is what current firmware ships.
Verified against a live OPNsense: job accepted (`{"result": "ok", "uuid": …}`), `10.30.0.1` answered 3 of 3 at 0.116 ms.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Symptom
Every
ping()and everyping_sweep()probe fails against a live OPNsense:Found 2026-08-22 through netOrk, where a LAN scan from a firewall reported 254 hosts scanned and 0 alive, with no error at all.
Root cause
_ping_model_node()reads the model fromGET /api/diagnostics/ping/getand takes the single dict-valued key as the node to post under. The real response nests two levels:So the helper returned
"ping"and_ping_job_createposted{"ping": {hostname: …}}, putting the fields where OPNsense expects thesettingsnode. The hostname never arrived, and the API said so on every single call.Why the tests passed
FakePingAPIintests/unit/test_ping.pyanswered/getwith a one-level model,{node: {"hostname": "", "fam": "ip"}}, and read the posted payload back through the same assumption. The fake agreed with the code about a shape neither shares with the device.Fix
_ping_model_path()returns the whole chain of single-dict wrappers (("ping", "settings")), stopping at the first level with more than one key — i.e. the field level, wherefamis a dict too and descending further would land inside a form field._wrap_in_model()nests the settings accordingly. Both the two-level and a hypothetical one-level model work, and the default when/getcannot be read is what current firmware ships.Verified against a live OPNsense: job accepted (
{"result": "ok", "uuid": …}),10.30.0.1answered 3 of 3 at 0.116 ms.