tailscale/tailscale · info
errNoSession
errNoSession
Error message
no-browser-session
What it means
errNoSession is a sentinel error from the Tailscale web client (client/web/auth.go) meaning the request carries no usable browser session. getSession returns it when the request has no session cookie at all, when the cookie value is not in the in-memory browserSessions map, when the session was created by a different node/user, or when the session expired (30 days by default). It is the normal 'not logged in yet' signal that drives the web client to its login flow.
Source
Thrown at client/web/auth.go:76
case s.isExpired(now):
return false // expired
}
return true
}
// isExpired reports true if s is expired.
// 2023-10-05: Sessions expire by default 30 days after creation.
func (s *browserSession) isExpired(now time.Time) bool {
return !s.Created.IsZero() && now.After(s.expires())
}
// expires reports when the given session expires.
func (s *browserSession) expires() time.Time {
return s.Created.Add(sessionCookieExpiry)
}
var (
errNoSession = errors.New("no-browser-session")
errNotUsingTailscale = errors.New("not-using-tailscale")
errTaggedRemoteSource = errors.New("tagged-remote-source")
errTaggedLocalSource = errors.New("tagged-local-source")
errNotOwner = errors.New("not-owner")
)
// getSession retrieves the browser session associated with the request,
// if one exists.
//
// An error is returned in any of the following cases:
//
// - (errNotUsingTailscale) The request was not made over tailscale.
//
// - (errNoSession) The request does not have a session.
//
// - (errTaggedRemoteSource) The source is remote (another node) and tagged.
// Users must use their own user-owned devices to manage other nodes'
// web clients.View on GitHub (pinned to 6e0912f979)
Solutions
- Complete the web client login flow to obtain a fresh session cookie before calling protected endpoints
- Ensure every request sends the session cookie (same browser profile, credentials: 'include' for fetch/XHR)
- If the web client process restarted, log in again - sessions are in-memory only
- If the node was re-logged-in as a different identity, log in again so the session is re-bound to the current node/user
Example fix
// before
http.HandleFunc("/api/", s.authed(func(w, r) {...})) // 500 on first visit
// after
http.HandleFunc("/api/", func(w http.ResponseWriter, r *http.Request) {
_, _, _, err := s.getSession(r)
if errors.Is(err, errNoSession) {
http.Redirect(w, r, "/login", http.StatusTemporaryRedirect)
return
}
// proceed
}) Defensive patterns
Strategy: try-catch
Validate before calling
// before hitting protected web-client routes, check the cookie exists
if _, err := r.Cookie("tailscale-webclient-session"); errors.Is(err, http.ErrNoCookie) {
http.Redirect(w, r, "/login", http.StatusTemporaryRedirect)
return
} Type guard
func isNoSession(err error) bool {
return err != nil && errors.Is(err, errNoSession) // within package web; externally: err.Error() == "no-browser-session"
} Try / catch
sess, whois, status, err := s.getSession(r)
switch {
case errors.Is(err, errNoSession):
// expected state: send the user through the login flow, do not log as 5xx
http.Redirect(w, r, "/login", http.StatusTemporaryRedirect)
return
case err != nil:
http.Error(w, err.Error(), http.StatusInternalServerError)
return
} Prevention
- Treat errNoSession as a redirect trigger, not a server error
- Ensure front-end fetch/XHR calls include credentials so the session cookie travels
- Expect all sessions to be lost on web-client restart (in-memory store) and re-authenticate
- Handle the 30-day sessionCookieExpiry in long-lived dashboards by re-login on 401-equivalent
When it happens
Trigger: Calling a web client handler before completing the browser-based login; cookie deleted or not sent (different browser, incognito, cross-origin fetch without credentials); the tailscale/web client process restarted so the in-memory sync.Map session store is empty; the session hit sessionCookieExpiry; or the source node/user changed since login (machine logged out and back in as a different identity).
Common situations: First visit to the web UI; long-lived tab whose 30-day session expired; server restart wiping sessions while the browser still holds the cookie; automated clients that hit web client routes without ever performing the login handshake.
Related errors
- errNotUsingTailscale
- errTaggedRemoteSource
- errTaggedLocalSource
- errNotOwner
- missing self node in tailscale status
AI-assisted analysis of tailscale/tailscale@6e0912f979 (2026-08-18).
Data as JSON: /api/errors/465fa11e4c4f04db.
Report an issue: GitHub.