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)
returnView on GitHub (pinned to 6e0912f979)
Solutions
- Always include addr, e.g. /localapi/v0/whois?addr=100.101.102.103.
- Guard in the caller: if the address variable is empty, fail early with your own error instead of issuing the request.
- 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
- Build query strings with url.Values so parameters are never silently dropped.
- Reject empty lookup inputs at your own API boundary with a clearer message than the server's.
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
- invalid nodekey in 'addr' parameter
- invalid 'addr' parameter
- invalid svc_name
- invalid dst_ip
- no SNI ServerName
AI-assisted analysis of tailscale/tailscale@6e0912f979 (2026-08-18).
Data as JSON: /api/errors/2b85884f09f6cd4a.
Report an issue: GitHub.