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)
		return

View on GitHub (pinned to 6e0912f979)

Solutions

  1. Send Connection: upgrade and Upgrade: ts-dial headers
  2. Use the tailscale CLI/library dial support that speaks the protocol
  3. 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.