tailscale/tailscale · error
peer %v not authorized (not found in local tailscaled)
Error message
peer %v not authorized (not found in local tailscaled)
What it means
With -verify-clients (verifyClientsLocalTailscaled), Server.verifyClient (derpserver.go:1593) asks the local tailscaled's WhoIsNodeKey about the connecting node; local.ErrPeerNotFound maps to this explicit refusal. It means the node key is not present in the tailnet that the derper host's tailscaled belongs to, so the client is rejected before relaying anything. Note the mesh-peer fast path above it: peers with the mesh token skip this check entirely.
Source
Thrown at derp/derpserver/derpserver.go:1612
// verifyClient checks whether the client is allowed to connect to the derper,
// depending on how & whether the server's been configured to verify.
func (s *Server) verifyClient(ctx context.Context, clientKey key.NodePublic, info *derp.ClientInfo, clientIP netip.Addr) error {
if s.isMeshPeer(info) {
// Trusted mesh peer. No need to verify further. In fact, verifying
// further wouldn't work: it's not part of the tailnet so tailscaled and
// likely the admission control URL wouldn't know about it.
return nil
}
if info != nil && s.disallowedAppNames.Contains(info.AppName) {
return fmt.Errorf("disallowed app name %q", info.AppName)
}
// tailscaled-based verification:
if s.verifyClientsLocalTailscaled {
_, err := s.localClient.WhoIsNodeKey(ctx, clientKey)
if err == local.ErrPeerNotFound {
return fmt.Errorf("peer %v not authorized (not found in local tailscaled)", clientKey)
}
if err != nil {
if strings.Contains(err.Error(), "invalid 'addr' parameter") {
// Issue 12617
return errors.New("tailscaled version is too old (out of sync with derper binary)")
}
return fmt.Errorf("failed to query local tailscaled status for %v: %w", clientKey, err)
}
}
// admission controller-based verification:
if s.verifyClientsURL != "" {
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
jreq, err := json.Marshal(&tailcfg.DERPAdmitClientRequest{
NodePublic: clientKey,
Source: clientIP,View on GitHub (pinned to a7769cbc33)
Solutions
- Log the derper host's tailscaled into the same tailnet as the nodes connecting to it
- Re-add or re-authenticate the rejected node in the control plane and retry
- If one derper must serve multiple tailnets, use -verify-clients-url admission control that decides per node key
- Otherwise point the node at a DERP server whose verification path accepts it, or stop running with -verify-clients
Defensive patterns
Strategy: validation
Validate before calling
// Pre-flight: would the local tailscaled know this node?
if _, err := localClient.WhoIsNodeKey(ctx, nodeKey); err != nil {
if errors.Is(err, local.ErrPeerNotFound) {
return fmt.Errorf("node %v is not in this tailnet; fix tailnet membership before pointing it here", nodeKey)
}
return fmt.Errorf("tailscaled check failed: %w", err)
} Type guard
// Distinguish 'unknown peer' from other local-API failures
func isPeerNotFound(err error) bool {
return errors.Is(err, local.ErrPeerNotFound)
} Try / catch
In Go: _, err := s.localClient.WhoIsNodeKey(ctx, clientKey); switch { case errors.Is(err, local.ErrPeerNotFound): reject as unauthorized; case err != nil: surface as infrastructure failure with retry; default: admit } — the code already splits the first case; keep the distinction visible to operators. Prevention
- Keep the derper host's tailscaled in the same tailnet as its clients
- Use --verify-clients-url when one derper serves multiple tailnets
- Re-check node membership after tailnet resets or user removals
When it happens
Trigger: A node from a different tailnet connects; the derper host's tailscaled is logged into another tailnet than the connecting nodes; the node was removed or its key expired; headscale serving multiple tailnets through one derper.
Common situations: Shared derper fronting multiple tailnets without an admission controller; derper container whose tailscaled sidecar joined the wrong tailnet; stale node keys after a tailnet reset retrying the old DERP map.
Related errors
- client %v rejected: %v
- failed to query local tailscaled status for %v: %w
- insufficient permissions
- errTaggedRemoteSource
- errTaggedLocalSource
AI-assisted analysis of tailscale/tailscale@a7769cbc33 (2026-08-18).
Data as JSON: /api/errors/418ec84437b3e581.
Report an issue: GitHub.