Packets on ROUTING_APP (portnum 5) that are successful acknowledgements are rendered in the UI as:
error_reason: NONE
This reads as an error to anyone scanning the packet list or a node's packet detail page, when it is in fact the opposite — it is the implicit ACK. Since ROUTING packets are one of the highest-volume packet types on a busy mesh, the packet list ends up looking like it is full of errors.
Cause
Packet.from_model() in meshview/web.py renders any decoded protobuf via the generic branch:
ifpayloadisNone:text_payload="Did not decode"elifisinstance(payload,Message):text_payload=text_format.MessageToString(payload)
For Routing, the error_reason field lives inside a protobuf oneof (variant). Fields inside a oneof have explicit presence, so error_reason = NONE (value 0) is still written to the wire and is still reported as set by WhichOneof(). text_format.MessageToString() therefore prints it verbatim as error_reason: NONE, instead of omitting it the way it would for a normal scalar zero value.
In other words, NONE here is not "no data" — it is the ACK, and the current rendering gives it the exact opposite meaning to the reader.
Evidence
Captured live from MQTT (msh/EU_868/#), 41 ROUTING_APP packets:
error_reason
count
current render
meaning
NONE
19
error_reason: NONE
ACK (success)
NO_RESPONSE
22
error_reason: NO_RESPONSE
NAK (failure)
All 41 had request_id set, confirming they are responses to a prior packet rather than route-discovery requests.
Point Meshview at any reasonably active MQTT broker.
Open the firehose / packet list, or any node page with routing traffic.
Filter to portnum ROUTING_APP.
Observe that successful ACKs are displayed as error_reason: NONE.
Expected behaviour
error_reason == NONE should display as ACK.
Any other error_reason should display as NAK: <REASON> (e.g. NAK: NO_RESPONSE), so genuine failures remain visible and distinguishable.
route_request / route_reply variants of Routing should keep their existing rendering.
Environment
Meshview 3.0.7 (31ca098)
Python 3.13
SQLite backend
Proposed fix
Special-case the error_reason variant of Routing in Packet.from_model() before falling through to the generic MessageToString() branch. Happy to open a PR for this.
Packets on `ROUTING_APP` (portnum 5) that are successful acknowledgements are rendered in the UI as:
```
error_reason: NONE
```
This reads as an error to anyone scanning the packet list or a node's packet detail page, when it is in fact the opposite — it is the implicit ACK. Since ROUTING packets are one of the highest-volume packet types on a busy mesh, the packet list ends up looking like it is full of errors.
## Cause
`Packet.from_model()` in `meshview/web.py` renders any decoded protobuf via the generic branch:
```python
if payload is None:
text_payload = "Did not decode"
elif isinstance(payload, Message):
text_payload = text_format.MessageToString(payload)
```
For `Routing`, the `error_reason` field lives inside a protobuf `oneof` (`variant`). Fields inside a `oneof` have explicit presence, so `error_reason = NONE` (value `0`) is still written to the wire and is still reported as set by `WhichOneof()`. `text_format.MessageToString()` therefore prints it verbatim as `error_reason: NONE`, instead of omitting it the way it would for a normal scalar zero value.
In other words, `NONE` here is not "no data" — it is the ACK, and the current rendering gives it the exact opposite meaning to the reader.
## Evidence
Captured live from MQTT (`msh/EU_868/#`), 41 `ROUTING_APP` packets:
| `error_reason` | count | current render | meaning |
|---|---|---|---|
| `NONE` | 19 | `error_reason: NONE` | ACK (success) |
| `NO_RESPONSE` | 22 | `error_reason: NO_RESPONSE` | NAK (failure) |
All 41 had `request_id` set, confirming they are responses to a prior packet rather than route-discovery requests.
Sample decoded packet (routing payload `1800` = field 3 varint 0 = `error_reason: NONE`):
```json
{
"topic": "msh/EU_868/2/e/MediumFast/!f6fd6c78",
"packet_id": 2621580621,
"from": 1849866277,
"to": 3932882981,
"request_id": 2320884292,
"routing_payload_hex": "1800",
"variant": "error_reason",
"error_reason": "NONE",
"rendered_as": "error_reason: NONE"
}
```
## Steps to reproduce
1. Point Meshview at any reasonably active MQTT broker.
2. Open the firehose / packet list, or any node page with routing traffic.
3. Filter to portnum `ROUTING_APP`.
4. Observe that successful ACKs are displayed as `error_reason: NONE`.
## Expected behaviour
- `error_reason == NONE` should display as `ACK`.
- Any other `error_reason` should display as `NAK: <REASON>` (e.g. `NAK: NO_RESPONSE`), so genuine failures remain visible and distinguishable.
- `route_request` / `route_reply` variants of `Routing` should keep their existing rendering.
## Environment
- Meshview 3.0.7 (`31ca098`)
- Python 3.13
- SQLite backend
## Proposed fix
Special-case the `error_reason` variant of `Routing` in `Packet.from_model()` before falling through to the generic `MessageToString()` branch. Happy to open a PR for this.
<!-- github-sync-id:5351665191 -->
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.
Packets on
ROUTING_APP(portnum 5) that are successful acknowledgements are rendered in the UI as:This reads as an error to anyone scanning the packet list or a node's packet detail page, when it is in fact the opposite — it is the implicit ACK. Since ROUTING packets are one of the highest-volume packet types on a busy mesh, the packet list ends up looking like it is full of errors.
Cause
Packet.from_model()inmeshview/web.pyrenders any decoded protobuf via the generic branch:For
Routing, theerror_reasonfield lives inside a protobufoneof(variant). Fields inside aoneofhave explicit presence, soerror_reason = NONE(value0) is still written to the wire and is still reported as set byWhichOneof().text_format.MessageToString()therefore prints it verbatim aserror_reason: NONE, instead of omitting it the way it would for a normal scalar zero value.In other words,
NONEhere is not "no data" — it is the ACK, and the current rendering gives it the exact opposite meaning to the reader.Evidence
Captured live from MQTT (
msh/EU_868/#), 41ROUTING_APPpackets:error_reasonNONEerror_reason: NONENO_RESPONSEerror_reason: NO_RESPONSEAll 41 had
request_idset, confirming they are responses to a prior packet rather than route-discovery requests.Sample decoded packet (routing payload
1800= field 3 varint 0 =error_reason: NONE):Steps to reproduce
ROUTING_APP.error_reason: NONE.Expected behaviour
error_reason == NONEshould display asACK.error_reasonshould display asNAK: <REASON>(e.g.NAK: NO_RESPONSE), so genuine failures remain visible and distinguishable.route_request/route_replyvariants ofRoutingshould keep their existing rendering.Environment
31ca098)Proposed fix
Special-case the
error_reasonvariant ofRoutinginPacket.from_model()before falling through to the genericMessageToString()branch. Happy to open a PR for this.fix added to version 3.0.8,