tsnet: don't panic in Close when Start failed before s.sys was set
Server.close() dereferenced s.sys.Bus unconditionally. s.sys is only assigned partway through doInit, so it is still nil when Start fails before that point, and Close then panics with a nil pointer dereference. Close is reachable in that state: Start is idempotent via initOnce and returns the stored initErr, so callers that `defer Close()` before checking the error land in close() with s.sys == nil. Every other resource released nearby (s.dialer, s.localAPIListener, s.loopbackListener) is already nil-guarded; this line was the outlier. This is reachable in practice from libtailscale/TailscaleKit, where a failed tsnet start on darwin is followed by a close and surfaces as EXC_BAD_ACCESS rather than the underlying start error. Use GetOK instead of Get as well, so an unset Bus returns ok=false rather than panicking via SubSystem.Get. Adds TestCloseBeforeStart, which panics without this change. Updates #20988 Signed-off-by: Prakash Rudraraju <1471544+prakashrj@users.noreply.github.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
P
Prakash Rudraraju committed
ec2ca5964b1693652d1c5bb83b6f3d6173fbd44d
Parent: d7253cb
Committed by Brad Fitzpatrick <brad@danga.com>
on 8/26/2026, 8:47:29 PM