slackhq/nebula · error
could not enable TCP SACK: %v
Error message
could not enable TCP SACK: %v
What it means
In netstack New (service/service.go), after creating the gVisor ipstack, TCP SACK is enabled via SetTransportProtocolOption; any tcpip error is fatal and reported as this message. SACK is required for the tunneled TCP stack to behave well, so the library fails fast.
Source
Thrown at service/service.go:80
}
}()
ctx := control.Context()
eg, ctx := errgroup.WithContext(ctx)
s := Service{
eg: eg,
control: control,
}
s.mu.listeners = map[uint16]*tcpListener{}
s.ipstack = stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol},
TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol, icmp.NewProtocol4, icmp.NewProtocol6},
})
sackEnabledOpt := tcpip.TCPSACKEnabled(true) // TCP SACK is disabled by default
tcpipErr := s.ipstack.SetTransportProtocolOption(tcp.ProtocolNumber, &sackEnabledOpt)
if tcpipErr != nil {
return nil, fmt.Errorf("could not enable TCP SACK: %v", tcpipErr)
}
linkEP := channel.New( /*size*/ 512 /*mtu*/, 1280, "")
if tcpipProblem := s.ipstack.CreateNIC(nicID, linkEP); tcpipProblem != nil {
return nil, fmt.Errorf("could not create netstack NIC: %v", tcpipProblem)
}
ipv4Subnet, _ := tcpip.NewSubnet(tcpip.AddrFrom4([4]byte{0x00, 0x00, 0x00, 0x00}), tcpip.MaskFrom(strings.Repeat("\x00", 4)))
s.ipstack.SetRouteTable([]tcpip.Route{
{
Destination: ipv4Subnet,
NIC: nicID,
},
})
ipNet := device.Networks()
pa := tcpip.ProtocolAddress{
AddressWithPrefix: tcpip.AddrFromSlice(ipNet[0].Addr().AsSlice()).WithPrefix(),
Protocol: ipv4.ProtocolNumber,
}View on GitHub (pinned to dd8f660c0a)
Solutions
- Ensure tcp.NewProtocol is included in TransportProtocols when creating the stack
- Update or pin the gVisor dependency to a version compatible with this code
- Rebuild with the unmodified stack configuration from service/service.go
- Capture the wrapped tcpipErr (%v) to identify the exact tcpip error code
Example fix
// before
TransportProtocols: []stack.TransportProtocolFactory{udp.NewProtocol, icmp.NewProtocol4, icmp.NewProtocol6}
// after
TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol, icmp.NewProtocol4, icmp.NewProtocol6} Defensive patterns
Strategy: try-catch
Validate before calling
// ensure protocols registered before New:
stacks := []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol, icmp.NewProtocol4, icmp.NewProtocol6} Try / catch
svc, err := service.New(...)
if err != nil {
if strings.Contains(err.Error(), "TCP SACK") {
log.Fatalf("netstack TCP setup failed (check gVisor deps/protocol registration): %v", err)
}
return err
} Prevention
- Keep tcp.NewProtocol registered when customizing stack creation
- Pin gVisor versions; test after dependency upgrades
- Log the wrapped tcpip error code for diagnosis
When it happens
Trigger: service.New is called (via doService, run, or newSimpleService) and the gVisor netstack rejects the TCPSACKEnabled option — practically only when the TCP protocol was not registered on the stack or a gVisor version/API incompatibility exists.
Common situations: Custom builds where tcp.NewProtocol was dropped from TransportProtocols; vendored gVisor fork with different option semantics; upstream gVisor API change breaking SetTransportProtocolOption.
Related errors
- could not create netstack NIC: %v
- error creating IP: %s
- unknown network type: %s
- only wildcard address supported, got %q %v
- invalid port %d
AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03).
Data as JSON: /api/errors/8b4ba36057a90da1.
Report an issue: GitHub.