tailscale/tailscale · error
exit node cannot be changed: %w
Error message
exit node cannot be changed: %w
What it means
checkPrefs: the user tried to change exit node prefs (ExitNodeID/ExitNodeIP/AutoExitNode) that are managed by syspolicy, and either the AllowExitNodeOverride policy did not permit the override or the user attempted to disable exit node usage entirely. A policy-enforced rejection.
Source
Thrown at ipn/ipnlocal/local.go:5224
// Prevent users from changing exit node preferences
// when exit node usage is managed by policy.
if mp.ExitNodeIDSet || mp.ExitNodeIPSet || mp.AutoExitNodeSet {
isManaged, err := b.polc.HasAnyOf(pkey.ExitNodeID, pkey.ExitNodeIP)
if err != nil {
err = fmt.Errorf("policy check failed: %w", err)
} else if isManaged {
// Allow users to override ExitNode policy settings and select an exit node manually
// if permitted by [pkey.AllowExitNodeOverride].
//
// Disabling exit node usage entirely is not allowed.
allowExitNodeOverride, _ := b.polc.GetBoolean(pkey.AllowExitNodeOverride, false)
if !allowExitNodeOverride || b.changeDisablesExitNodeLocked(prefs, mp) {
err = errManagedByPolicy
}
}
if err != nil {
errs = append(errs, fmt.Errorf("exit node cannot be changed: %w", err))
}
}
return errors.Join(errs...)
}
// changeDisablesExitNodeLocked reports whether applying the change
// to the given prefs would disable exit node usage.
//
// In other words, it returns true if prefs.ExitNodeID is non-empty
// initially, but would become empty after applying the given change.
//
// It applies the same adjustments and resolves the exit node in the prefs
// as done during actual edits. While not optimal performance-wise,
// changing the exit node via LocalAPI isn't a hot path, and reusing
// the same logic ensures consistency and simplifies maintenance.
//
// b.mu must be held.View on GitHub (pinned to 5201273aec)
Solutions
- Disconnect or adjust the conflicting setting before changing the exit node.
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at ipn/ipnlocal/local.go:5218 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/b25daaa5f5e74c1a.
Report an issue: GitHub.