tailscale/tailscale · error
decoding register response: %w
Error message
decoding register response: %w
What it means
The 200 body is not valid JSON for tailcfg.RegisterResponse: json.Unmarshal failed after transport and size checks passed. The content is wrong rather than unreachable: typically an HTML error page, an empty body, or output from an incompatible server version.
Source
Thrown at control/tsp/register.go:110
if res.StatusCode != 200 {
msg, _ := io.ReadAll(io.LimitReader(res.Body, maxResponseSize))
return nil, fmt.Errorf("register request: http %d: %.200s",
res.StatusCode, strings.TrimSpace(string(msg)))
}
// Read up to maxResponseSize+1 so we can distinguish "exactly at cap" from
// "over the cap" rather than relying on a truncated json parse error.
data, err := io.ReadAll(io.LimitReader(res.Body, maxResponseSize+1))
if err != nil {
return nil, fmt.Errorf("reading register response: %w", err)
}
if int64(len(data)) > maxResponseSize {
return nil, fmt.Errorf("register response exceeds max %d", maxResponseSize)
}
var resp tailcfg.RegisterResponse
if err := json.Unmarshal(data, &resp); err != nil {
return nil, fmt.Errorf("decoding register response: %w", err)
}
if resp.Error != "" {
return nil, fmt.Errorf("register: %s", resp.Error)
}
return &resp, nil
}
View on GitHub (pinned to 6e0912f979)
Solutions
- Log the raw body before Unmarshal in a debug build to see what actually arrived
- Remove or fix middleboxes between client and control server
- Verify the server implements the register protocol version this client expects
Defensive patterns
Strategy: try-catch
Try / catch
if err != nil {
var syn *json.SyntaxError
if errors.As(err, &syn) {
// body was not JSON: inspect what the server or proxy actually returned
}
} Prevention
- Keep middleboxes from rewriting control-plane response bodies
- Pin and verify server versions in the deployment so schema output is predictable
When it happens
Trigger: A proxy or CDN intercepting and rewriting the response; a control server speaking a different schema; a body mangled or truncated in transit.
Common situations: TLS-intercepting middleboxes; ingress returning 200 with an error page; version skew with a forked control server.
Related errors
- decoding server key: %w
- invalid dns.OSConfig: %w
- invalid query response: %w
- invalid derp map json: %w
- invalid JSON from check-so-mark-in-use: %w
AI-assisted analysis of tailscale/tailscale@6e0912f979 (2026-08-18).
Data as JSON: /api/errors/bc482537cb416bdb.
Report an issue: GitHub.