From 78c4511a3d78003c1a14b241701633fb4ca61fcf Mon Sep 17 00:00:00 2001 From: Codinget Date: Tue, 16 Jun 2026 08:08:32 +0000 Subject: [PATCH] fix(tsconnect): avoid nil services slice in netmap JSON userServicesFromView returned a nil slice when a node advertised no services, which (combined with the omitempty tag) caused the services field to be dropped or serialize as null instead of []. TypeScript declares services as a non-optional array, so JS callers calling .find()/.some() on it would throw intermittently depending on which netmap snapshot they observed. Co-Authored-By: Claude Sonnet 4.6 --- cmd/tsconnect/wasm/wasm_js.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/tsconnect/wasm/wasm_js.go b/cmd/tsconnect/wasm/wasm_js.go index 2744eeb34..c3319a873 100644 --- a/cmd/tsconnect/wasm/wasm_js.go +++ b/cmd/tsconnect/wasm/wasm_js.go @@ -1393,7 +1393,7 @@ func (i *jsIPN) setServices(jsServices js.Value) js.Value { // userServicesFromView converts a hostinfo services slice to jsService entries, // filtering out internal peerapi protocol entries (already reflected in peerAPIURL). func userServicesFromView(svcs views.Slice[tailcfg.Service]) []jsService { - var out []jsService + out := make([]jsService, 0, svcs.Len()) for _, s := range svcs.All() { switch s.Proto { case tailcfg.PeerAPI4, tailcfg.PeerAPI6, tailcfg.PeerAPIDNS: @@ -1652,7 +1652,7 @@ type jsNetMapNode struct { MachineKey string `json:"machineKey"` NodeKey string `json:"nodeKey"` PeerAPIURL string `json:"peerAPIURL,omitempty"` - Services []jsService `json:"services,omitempty"` + Services []jsService `json:"services"` } type jsNetMapSelfNode struct {