Repository navigation
TCP config writes are silently lost on Linux as well (2.7.11, ESP32-S3) #985
Description
Summary
--set over TCP is silently lost against an ESP32-S3 node on Linux, with 2.7.11 — the
release that contains the #958 fix. #957 concluded the loss was Windows-only ("Same
commands work on Linux"); on this hardware it is not.
Two nodes, two client machines, verified by reading the value back over the other
transport rather than the one the write took.
Setup
One of the nodes has both USB serial and WiFi up, so the same device can be written one
way and read the other. Seeed XIAO ESP32S3, firmware 2.8.0.74578a8 (a build of
develop; I have not tried a release build). Clients: Debian 12 and CachyOS, both with
meshtastic 2.7.11 from PyPI.
| write | read back over | result |
|---|---|---|
--host <ip> --set display.screen_on_secs N |
serial | unchanged, 6 of 6 |
--host <ip> --set … --wait-to-disconnect 8 |
serial | applied |
--port /dev/ttyACM0 --set … |
TCP | applied |
A second unit of the same model behaves the same over TCP.
The CLI reports success either way: it prints Set display.screen_on_secs to N,
Writing modified preferences to device and Writing display configuration to device
even when nothing is written. A BrokenPipeError sometimes precedes that and sometimes
does not, so it is not a usable signal — and it also shows up on successful reads, which
is #962.
What differs between the two transports
--debug shows both building the same packet — ADMIN_APP to the node's own number,
pki_encrypted: true, want_ack: true — both obtaining a sessionkey, and both logging
Wrote: display.
The difference is what comes back. Over serial:
ROUTING_APP … request_id: … routing { errorReason: NONE }
Over TCP nothing comes back; the CLI sends disconnect: true and closes first. The
device logs, at that moment:
[E][NetworkClient.cpp:435] write(): fail on fd 52, errno: 104, "Connection reset by peer"
That is the node failing to send the ack above.
Why the #958 fix does not cover this
Raising GRACEFUL_CLOSE_TIMEOUT from 0.25 to 5.0 changes nothing — two further writes,
both lost. _wait_for_reader_exit() returns as soon as the reader exits, and the
disconnect: true sent just before close causes exactly that. The half-close does
happen; the wait is simply over long before the device has applied the admin packet.
One thing worth separating while you are in there: #957 attributes the loss to Winsock
discarding received-but-unread data on RST. The receive buffer that matters here is the
device's, not the client's — the RST arrives at the ESP32. That would explain why a
client-side platform distinction does not hold up.
Ruled out
- A second client competing for the node's TCP server — same result with our
once-a-minute telemetry poller stopped. - The field or the value — several fields, plain integers, same outcome.
- One bad node — two units, and two different Linux clients.
Workaround
--wait-to-disconnect 8 makes the write land every time. Useful for anyone scripting
against a node over the network in the meantime.
Activity
Simplycissmus commentedon Sep 19, 2026
Also affected:
--setlat/--setlon/--setalt, so this is not specific to--setbut to one-shot admin writes over TCP generally.
Measured on the same node, written over TCP and read back over serial:
--wait-to-disconnect 20One caveat on method, in case anyone reproduces this: an earlier run of the same test
was worthless because the S3's USB CDC had disappeared mid-run while the node kept
serving over WiFi. The serial read-back then returns empty strings rather than an
error, which reads exactly like "not applied". I now gate each run on a probe that has
to return a known non-empty value first, and treat an empty read as a void measurement
rather than a negative one. Worth mentioning because the obvious way to verify this bug
is the one that misleads.