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
- Check SupportsSplitDNS() on the manager before requesting split DNS, and fall back to full-tunnel DNS (set Nameservers only, leave Routes empty).
- Upgrade the machine to Windows 10+ so the NRPT path is available.
- 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
- Gate split-DNS configuration on SupportsSplitDNS().
- Know your fleet's minimum Windows version before shipping NRPT-dependent policy.
- Prefer global-resolver fallbacks on legacy hosts.
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
- opening %q: %w
- no matching certificate
- cannot find tailscale.exe alongside %s: %w
- expected %s to exist, got stat error: %w
- expected %s to be a directory; got %v
AI-assisted analysis of tailscale/tailscale@6e0912f979 (2026-08-18).
Data as JSON: /api/errors/44b1119478b6721f.
Report an issue: GitHub.