GH-101: fix(discord): nil-conn deref in listen crashes process on network failure — snapshot conn under lock, no lockless g.conn.ReadJSON#102
Merged
Conversation
…crash (GH-101) The gateway read loop called g.conn.ReadJSON directly with no lock and no nil-guard, while the reconnect/close path nils g.conn under g.mu. A network failure landing mid-iteration crashed the process with a SIGSEGV inside gorilla/websocket's NextReader on a nil *Conn. listen now snapshots g.conn under g.mu once per iteration and reads via the local snapshot; a nil snapshot returns cleanly, which reconnectLoop already treats as a signal to reconnect. This also removes the data race on g.conn between listen and the reconnect/close path.
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.
Summary
Automated PR created by Pilot for task GH-101.
Closes #101
Changes
GitHub Issue GH-101: fix(discord): nil-conn deref in listen crashes process on network failure — snapshot conn under lock, no lockless g.conn.ReadJSON
Context
Live daemon crash (2026-07-14, panic → SIGSEGV): the Discord gateway
listengoroutine dereferences a nil websocket connection on network failure, crashing the whole process.Root cause (
sdk/integrations/discord/transport.go):listenat :190 callsg.conn.ReadJSON(&event)with NO nil-guard and NO lock (the func comment even says "Must NOT hold g.mu"). Meanwhile the reconnect/close path nils it::88-89 g.conn.Close(); g.conn = nil. So a network failure nilsg.connwhile the read loop is mid-iteration →ReadJSONon nil →NextReader(0x0)→ segfault. Note :144 (heartbeat) and :160 (resume) DO nil-checkg.conn; :190 (the read loop) does not — inconsistent.Implementation
In
sdk/integrations/discord/transport.go:listeniteration:g.mu.Lock(); c := g.conn; g.mu.Unlock(); if c == nil { <handle: return or wait-for-reconnect> }— then callc.ReadJSONon the localc, never the shared field. This removes both the nil-deref AND the data race ong.conn.g.connuses (lines ~98,102,126,149,167,190) for the same lockless-access pattern; make conn access uniformly lock-guarded/snapshotted.Acceptance
g.connduringlistendoes NOT panic — it returns cleanly or triggers reconnect (test: nil the conn mid-listen, assert no panic)-racetest on the listen/reconnect interleaving is clean (no data race on g.conn)g.connaccesses lock-guarded or snapshot-under-lockRefs
studio-sdk@v0.30.0/sdk/integrations/discord/transport.go:190vs guarded :144/:160