tailscale/tailscale · error
bad ts-dial upgrade
Error message
bad ts-dial upgrade
What it means
Returned as HTTP 400 by serveDial when the request is a POST but lacks the Connection: upgrade and Upgrade: ts-dial headers. The endpoint hijacks the connection into a raw TCP stream and refuses non-upgrade requests.
Source
Thrown at ipn/localapi/localapi.go:1318
}
res, err := h.b.Ping(ctx, ip, tailcfg.PingType(pingTypeStr), size)
if err != nil {
WriteErrorJSON(w, err)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(res)
}
func (h *Handler) serveDial(w http.ResponseWriter, r *http.Request) {
if r.Method != httpm.POST {
http.Error(w, "POST required", http.StatusMethodNotAllowed)
return
}
const upgradeProto = "ts-dial"
if !strings.Contains(r.Header.Get("Connection"), "upgrade") ||
r.Header.Get("Upgrade") != upgradeProto {
http.Error(w, "bad ts-dial upgrade", http.StatusBadRequest)
return
}
hostStr, portStr := r.Header.Get("Dial-Host"), r.Header.Get("Dial-Port")
if hostStr == "" || portStr == "" {
http.Error(w, "missing Dial-Host or Dial-Port header", http.StatusBadRequest)
return
}
network := cmp.Or(r.Header.Get("Dial-Network"), "tcp")
addr := net.JoinHostPort(hostStr, portStr)
// Check whether the resolved address is a Tailscale route.
// If not, tell the client to dial it directly so the connection
// comes from the calling user's UID rather than our root-owned daemon.
ipp, viaTailscale, err := h.b.Dialer().UserDialPlan(r.Context(), network, addr)
if err != nil {
http.Error(w, "resolve failure: "+err.Error(), http.StatusBadGateway)
returnView on GitHub (pinned to 6e0912f979)
Solutions
- Send Connection: upgrade and Upgrade: ts-dial headers
- Use the tailscale CLI/library dial support that speaks the protocol
- Verify intermediaries preserve the headers
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at ipn/localapi/localapi.go:1318 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of tailscale/tailscale@6e0912f979 (2026-08-18).
Data as JSON: /api/errors/70c1a9a6734fcb80.
Report an issue: GitHub.