tailscale/tailscale · error
failed to construct request: %s
Error message
failed to construct request: %s
What it means
The kube metrics server proxies incoming requests to tailscaled's LocalAPI usermetrics endpoint by first rebuilding the request with http.NewRequestWithContext(r.Context(), r.Method, url, r.Body). This constructor validates the method token and URL, so a malformed method (illegal characters, empty) or invalid target URL yields this 500 'failed to construct request'. It is a client-protocol error surfaced by the proxy, not a metrics failure.
Source
Thrown at kube/metrics/metrics.go:29
"fmt"
"io"
"net/http"
"tailscale.com/client/local"
"tailscale.com/client/tailscale/apitype"
)
// metrics is a simple metrics HTTP server, if enabled it forwards requests to
// the tailscaled's LocalAPI usermetrics endpoint at /localapi/v0/usermetrics.
type metrics struct {
debugEndpoint string
lc *local.Client
}
func proxy(w http.ResponseWriter, r *http.Request, url string, do func(*http.Request) (*http.Response, error)) {
req, err := http.NewRequestWithContext(r.Context(), r.Method, url, r.Body)
if err != nil {
http.Error(w, fmt.Sprintf("failed to construct request: %s", err), http.StatusInternalServerError)
return
}
req.Header = r.Header.Clone()
resp, err := do(req)
if err != nil {
http.Error(w, fmt.Sprintf("failed to proxy request: %s", err), http.StatusInternalServerError)
return
}
defer resp.Body.Close()
for key, val := range resp.Header {
for _, v := range val {
w.Header().Add(key, v)
}
}
w.WriteHeader(resp.StatusCode)
if _, err := io.Copy(w, resp.Body); err != nil {View on GitHub (pinned to 6e0912f979)
Solutions
- Send a normal, valid HTTP method (GET/POST) to the metrics endpoints
- If embedding this package, validate the configured debugEndpoint is a valid host:port before serving
- Reproduce with curl to confirm the raw request line is well-formed
Defensive patterns
Strategy: validation
Validate before calling
// Embedders: validate config before serving
if _, err := url.Parse("http://" + debugEndpoint + "/"); debugEndpoint != "" && err != nil {
return fmt.Errorf("invalid debug endpoint %q: %w", debugEndpoint, err)
} Try / catch
if resp.StatusCode == http.StatusInternalServerError && strings.Contains(body, "failed to construct request") {
// malformed method/URL — fix the client's request line; retrying the same bytes will fail identically
} Prevention
- Use standard HTTP verbs when scraping the metrics endpoints
- Validate any custom debugEndpoint/host:port at startup, not per request
When it happens
Trigger: A client issuing an HTTP request with an invalid method token (non-token bytes) that still reached the handler (e.g. via a frontend that relaxes validation), or an unusable target URL — in practice nearly impossible with the fixed http://LocalAPIHost/... URL unless the library is embedded and url/debugEndpoint are overridden with a malformed value.
Common situations: Embedding kube/metrics with a custom debug endpoint string that is not a valid absolute URL (missing scheme/host, control characters); exotic HTTP clients sending custom verbs with spaces.
Related errors
- failed to proxy request: %s
- debug endpoint not configured
- error writing capability version to state store: %w
- failed to store device ID in state: %w
- failed to store device FQDN in state: %w
AI-assisted analysis of tailscale/tailscale@6e0912f979 (2026-08-18).
Data as JSON: /api/errors/4e1e4b5beca1a1d3.
Report an issue: GitHub.