juanfont/headscale · warning

ErrUnsupportedClientVersion

ErrUnsupportedClientVersion

Error message

unsupported client version: %s (%d)

What it means

Returned during the early Noise stage when the client's declared protocol/capability version is below the minimum supported by this headscale build (isSupportedVersion check against capver data). The error names the mapped Tailscale version and raw capability number via ErrUnsupportedClientVersion.

Source

Thrown at hscontrol/noise.go:225

		r.Post("/c2n", ns.NotImplementedHandler)
	})

	ns.httpBaseConfig = &http.Server{
		Handler:           r,
		ReadHeaderTimeout: types.HTTPTimeout,
	}
	ns.http2Server = &http2.Server{}

	ns.http2Server.ServeConn(
		noiseConn,
		&http2.ServeConnOpts{
			BaseConfig: ns.httpBaseConfig,
		},
	)
}

func unsupportedClientError(version tailcfg.CapabilityVersion) error {
	return fmt.Errorf("%w: %s (%d)", ErrUnsupportedClientVersion, capver.TailscaleVersion(version), version)
}

func (ns *noiseServer) earlyNoise(protocolVersion int, writer io.Writer) error {
	if !isSupportedVersion(tailcfg.CapabilityVersion(protocolVersion)) {
		return unsupportedClientError(tailcfg.CapabilityVersion(protocolVersion))
	}

	earlyJSON, err := json.Marshal(&tailcfg.EarlyNoise{
		NodeKeyChallenge: ns.challenge.Public(),
	})
	if err != nil {
		return err
	}

	// 5 bytes that won't be mistaken for an HTTP/2 frame:
	// https://httpwg.org/specs/rfc7540.html#rfc.section.4.1 (Especially not
	// an HTTP/2 settings frame, which isn't of type 'T')
	var notH2Frame [5]byte

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Upgrade the client tailscaled to a recent stable release (check the version in the error message)
  2. Compare the client's capability version against hscontrol/capver data to find the minimum accepted
  3. If upgrading is impossible, pin an older headscale release that still supports that capability version
Defensive patterns

Strategy: validation

Validate before calling

if !isSupportedVersion(clientCapVer) {
    return fmt.Errorf("client version %s (%d) is below minimum supported; upgrade tailscaled",
        capver.TailscaleVersion(clientCapVer), clientCapVer)
}

Type guard

func isSupportedClient(v tailcfg.CapabilityVersion) bool {
    return isSupportedVersion(v)
}

Prevention

When it happens

Trigger: A tailscaled older than the server's minimum supported capability version connects via /ts2021; earlyNoise rejects it before any API traffic flows.

Common situations: Distro-packaged stale tailscale clients (e.g. 1.32-era) against a current headscale; embedded appliances that rarely update; after a headscale upgrade that raised the minimum capver.

Related errors


AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15). Data as JSON: /api/errors/a937e3accdd623a6. Report an issue: GitHub.