tailscale/tailscale · error

missing 'addr' parameter

Error message

missing 'addr' parameter

What it means

Returned as HTTP 400 by serveWhoIsWithBackend when the 'addr' query parameter is absent or empty (r.FormValue("addr") == ""). addr is the only lookup key for whois, so the handler refuses immediately instead of guessing. Note an empty-but-present parameter (addr=) hits the same path.

Source

Thrown at ipn/localapi/localapi.go:589

				http.Error(w, "invalid nodekey in 'addr' parameter", http.StatusBadRequest)
				return
			}
			n, u, ok = b.WhoIsNodeKey(k)
		} else if ip, err := netip.ParseAddr(v); err == nil {
			ipp = netip.AddrPortFrom(ip, 0)
		} else {
			var err error
			ipp, err = netip.ParseAddrPort(v)
			if err != nil {
				http.Error(w, "invalid 'addr' parameter", http.StatusBadRequest)
				return
			}
		}
		if ipp.IsValid() {
			n, u, ok = b.WhoIs(r.FormValue("proto"), ipp)
		}
	} else {
		http.Error(w, "missing 'addr' parameter", http.StatusBadRequest)
		return
	}
	if !ok {
		http.Error(w, "no match for IP:port", http.StatusNotFound)
		return
	}
	res := &apitype.WhoIsResponse{
		Node:        n.AsStruct(), // always non-nil per WhoIsResponse contract
		UserProfile: &u,           // always non-nil per WhoIsResponse contract
	}
	if n.Addresses().Len() > 0 {
		src := n.Addresses().At(0).Addr()
		switch {
		case r.FormValue("svc_name") != "":
			svcName := tailcfg.AsServiceName(r.FormValue("svc_name"))
			if svcName == "" {
				http.Error(w, "invalid svc_name", http.StatusBadRequest)
				return

View on GitHub (pinned to 6e0912f979)

Solutions

  1. Always include addr, e.g. /localapi/v0/whois?addr=100.101.102.103.
  2. Guard in the caller: if the address variable is empty, fail early with your own error instead of issuing the request.
  3. Log the full URL when whois fails so missing parameters are obvious.

Example fix

// before
q := url.Values{}
if ip != "" { q.Set("addr", ip) } // ip empty -> request without addr -> 400
res, _ := lc.DoLocalRequest(ctx, "GET", "/localapi/v0/whois?"+q.Encode(), nil)

// after
if ip == "" { return fmt.Errorf("whois: addr is required") }
q := url.Values{"addr": {ip}}
res, _ := lc.DoLocalRequest(ctx, "GET", "/localapi/v0/whois?"+q.Encode(), nil)
Defensive patterns

Strategy: validation

Validate before calling

if addr == "" {
    return errors.New("whois: 'addr' parameter is required")
}
q := url.Values{"addr": {addr}}
res, err := lc.DoLocalRequest(ctx, "GET", "/localapi/v0/whois?"+q.Encode(), nil)

Try / catch

if res.StatusCode == http.StatusBadRequest {
    if addr == "" { return errors.New("whois called without addr") }
    return fmt.Errorf("whois rejected addr %q", addr)
}

Prevention

When it happens

Trigger: GET /localapi/v0/whois with no query string; calling with only other valid parameters (proto=, dst_ip=, svc_name=) but no addr; query-string construction bug that drops addr when the value is empty.

Common situations: Template code that conditionally appends parameters and skips addr when a variable is unset; curl invocations where the '?' or '&' got mangled; clients ported from an API that defaulted to 'self'.

Related errors


AI-assisted analysis of tailscale/tailscale@6e0912f979 (2026-08-18). Data as JSON: /api/errors/2b85884f09f6cd4a. Report an issue: GitHub.