don't log the deliberate multicast re-bind as a socket error - #49
Merged
crgimenes merged 1 commit intoAug 20, 2026
Merged
Conversation
udpControlSupervisor closes the listener every udpRebindInterval to force
a fresh group join. That close is exactly what unblocks ReadFrom, so the
read loop always ends up in its error branch and logs the close it was
asked to cause -- a multicast listener printed
kutta: UDP control: read udp 0.0.0.0:9000: use of closed network connection
every 30s for as long as it ran. On an exhibit Pi left up for an
afternoon that was 375 lines of routine maintenance reported as failure,
and it reads convincingly like a listener that has died, which is
misleading in exactly the situation the log is there to help with.
Pass the read loop an atomic.Bool the supervisor sets just before a
deliberate close, so it can tell that case apart from a socket that
failed on its own. Real errors still log, including on the multicast
path: the flag is only ever set for the re-bind. Unicast callers pass
nil, since they never close early.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
crgimenes
approved these changes
Aug 20, 2026
crgimenes
left a comment
Owner
There was a problem hiding this comment.
Right diagnosis: the close is what unblocks ReadFrom, so the loop was reporting its own maintenance. Merging, thanks.
crgimenes
added a commit
that referenced
this pull request
Aug 20, 2026
Three loose ends from #42, #46 and #49, none of which change what those PRs set out to do. outboundInterface hardened: the deferred Close was unchecked (errcheck), the LocalAddr type assertion could panic, and a routing table with no default route to dial against made the multicast join fail outright -- which is exactly the exhibit case, a Pi wired straight to the sender with a static address and no gateway. It now falls back to the first interface that is up, not loopback and multicast-capable, so that machine joins on the one interface it has instead of refusing. udpControlLoop drops the atomic.Bool: net.ErrClosed already distinguishes a deliberate close from a socket that failed on its own, so the signature and the plumbing through both call sites go away with it. -max-kn rejects NaN, Inf and non-positive values, warning and keeping the default rather than printing a garbage legend. The default itself is now defaultMaxDisplayKn instead of a 25 repeated in two files.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
udpControlSupervisorcloses the listener everyudpRebindIntervalto force afresh group join. That close is exactly what unblocks
ReadFrom, so the readloop always lands in its error branch and logs the close it was asked to cause:
every 30s, for as long as a multicast listener runs. On an exhibit Pi left up
for an afternoon that was 375 lines of routine maintenance reported as failure.
It reads convincingly like a listener that has died, which is misleading in
exactly the situation the log exists to help with -- while debugging why an
ESP32 knob box wasn't reaching kutta, this cost me a detour before I realised
the socket was fine and the message was self-inflicted.
The fix passes the read loop an
atomic.Boolthat the supervisor sets justbefore a deliberate close, so it can tell that case apart from a socket that
failed on its own. Real errors still log, including on the multicast path --
the flag is only ever set for the re-bind, and is a fresh variable per
iteration. Unicast callers pass nil, since they never close early.
No behaviour change beyond the logging.
Verified with
gofmt,go vet,go build, andgo test -race ./..., plus onthe Raspberry Pi exhibit where the noise was found.