tailscale/tailscale · error

Split DNS unsupported on this Windows version

Error message

Split DNS unsupported on this Windows version

What it means

windowsManager.setSplitDNS installs NRPT (Name Resolution Policy Table) rules for split DNS, but the NRPT database is only created when isWindows10OrBetter() is true. If m.nrptDB is nil and resolvers are requested, the manager throws this error: the OS cannot express per-domain resolvers, so SetDNS fails rather than silently routing all queries through Tailscale. If resolvers is nil it is a no-op instead.

Source

Thrown at net/dns/manager_windows.go:174

func delValue(key registry.Key, name string) error {
	if err := key.DeleteValue(name); err != nil && err != registry.ErrNotExist {
		return err
	}
	return nil
}

// setSplitDNS configures one or more NRPT (Name Resolution Policy Table) rules
// to resolve queries for domains using resolvers, rather than the
// system's "primary" resolver.
//
// If no resolvers are provided, the Tailscale NRPT rules are deleted.
func (m *windowsManager) setSplitDNS(resolvers []netip.Addr, domains []dnsname.FQDN) error {
	if m.nrptDB == nil {
		if resolvers == nil {
			// Just a no-op in this case.
			return nil
		}
		return fmt.Errorf("Split DNS unsupported on this Windows version")
	}

	defer m.nrptDB.NotifyPolicyChanged()
	if len(resolvers) == 0 {
		return m.nrptDB.DelAllRuleKeys()
	}

	servers := make([]string, 0, len(resolvers))
	for _, resolver := range resolvers {
		servers = append(servers, resolver.String())
	}

	return m.nrptDB.WriteSplitDNSConfig(servers, domains)
}

func setTailscaleHosts(logf logger.Logf, prevHostsFile []byte, hosts []*HostEntry) ([]byte, error) {
	sc := bufio.NewScanner(bytes.NewReader(prevHostsFile))
	const (

View on GitHub (pinned to 6e0912f979)

Solutions

  1. Check SupportsSplitDNS() on the manager before requesting split DNS, and fall back to full-tunnel DNS (set Nameservers only, leave Routes empty).
  2. Upgrade the machine to Windows 10+ so the NRPT path is available.
  3. On the client, disable accepting split DNS (e.g. accept-dns=false) and use a plain resolver config for the old OS.

Example fix

// before — unconditional split DNS request
cfg := dns.OSConfig{
    Routes:    routes, // per-domain resolvers -> needs NRPT
    Addresses: addrs,
}
err := mgr.SetDNS(cfg)
// after — degrade gracefully on pre-Win10 hosts
if mgr.SupportsSplitDNS() {
    cfg.Routes = routes
}
err := mgr.SetDNS(cfg)
Defensive patterns

Strategy: validation

Validate before calling

// Ask the manager before requesting split DNS
if len(cfg.Routes) > 0 && !mgr.SupportsSplitDNS() {
    cfg.Routes = nil // degrade to full-tunnel DNS instead of failing SetDNS
}
err := mgr.SetDNS(cfg)

Try / catch

On 'Split DNS unsupported on this Windows version', retry SetDNS with config.Routes cleared and only global nameservers set — the NRPT-free path succeeds on the same OS.

Prevention

When it happens

Trigger: SetDNS is called with an OSConfig that includes split-DNS resolvers (config.Routes) on a Windows version older than Windows 10, where NewOSConfigurator left nrptDB nil.

Common situations: Running tailscaled on Windows Server 2012/2012 R2 or Windows 8.x; a MagicDNS/exit-node configuration that pushes per-domain resolvers to an old OS; policy or MDM configs assuming NRPT support on machines that predate it.

Related errors


AI-assisted analysis of tailscale/tailscale@6e0912f979 (2026-08-18). Data as JSON: /api/errors/44b1119478b6721f. Report an issue: GitHub.