tailscale/tailscale · error

could not enable TCP SACK: %v

Error message

could not enable TCP SACK: %v

What it means

netstack.Create asked gVisor to enable TCP selective acknowledgement on the freshly created stack and gVisor returned an error. SACK is disabled by default in gVisor; failure here is unexpected and points at a gVisor API change.

Source

Thrown at wgengine/netstack/netstack.go:360

		return nil, errors.New("nil logger")
	}
	if e == nil {
		return nil, errors.New("nil Engine")
	}
	if pm == nil {
		return nil, errors.New("nil proxymap.Mapper")
	}
	if dialer == nil {
		return nil, errors.New("nil Dialer")
	}
	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 := ipstack.SetTransportProtocolOption(tcp.ProtocolNumber, &sackEnabledOpt)
	if tcpipErr != nil {
		return nil, fmt.Errorf("could not enable TCP SACK: %v", tcpipErr)
	}
	// See https://github.com/tailscale/tailscale/issues/9707
	// gVisor's RACK performs poorly. ACKs do not appear to be handled in a
	// timely manner, leading to spurious retransmissions and a reduced
	// congestion window.
	tcpRecoveryOpt := tcpip.TCPRecovery(0)
	tcpipErr = ipstack.SetTransportProtocolOption(tcp.ProtocolNumber, &tcpRecoveryOpt)
	if tcpipErr != nil {
		return nil, fmt.Errorf("could not disable TCP RACK: %v", tcpipErr)
	}
	// gVisor defaults to reno at the time of writing. We explicitly set reno
	// congestion control in order to prevent unexpected changes. Netstack
	// has an int overflow in sender congestion window arithmetic that is more
	// prone to trigger with cubic congestion control.
	// See https://github.com/google/gvisor/issues/11632
	renoOpt := tcpip.CongestionControlOption("reno")
	tcpipErr = ipstack.SetTransportProtocolOption(tcp.ProtocolNumber, &renoOpt)
	if tcpipErr != nil {

View on GitHub (pinned to 5201273aec)

Solutions

  1. Check kernel support for TCP SACK; the wrapped error explains the rejection.
  2. Update gVisor/netstack or the host kernel to a version supporting the SACK option.
Defensive patterns

Strategy: try-catch

When it happens

Trigger: Thrown at wgengine/netstack/netstack.go:359 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of tailscale/tailscale@5201273aec (2026-08-18). Data as JSON: /api/errors/83277ec64a4022eb. Report an issue: GitHub.