{"record":{"id":"220e39b9dc42f299","repo":"tailscale/tailscale","slug":"reading-register-response-w","errorCode":null,"errorMessage":"reading register response: %w","messagePattern":"reading register response: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"control/tsp/register.go","lineNumber":103,"sourceCode":"\tres, err := nc.Do(req)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"register request: %w\", err)\n\t}\n\tdefer res.Body.Close()\n\n\tmaxResponseSize := cmp.Or(opts.MaxResponseSize, DefaultMaxMessageSize)\n\n\tif res.StatusCode != 200 {\n\t\tmsg, _ := io.ReadAll(io.LimitReader(res.Body, maxResponseSize))\n\t\treturn nil, fmt.Errorf(\"register request: http %d: %.200s\",\n\t\t\tres.StatusCode, strings.TrimSpace(string(msg)))\n\t}\n\n\t// Read up to maxResponseSize+1 so we can distinguish \"exactly at cap\" from\n\t// \"over the cap\" rather than relying on a truncated json parse error.\n\tdata, err := io.ReadAll(io.LimitReader(res.Body, maxResponseSize+1))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"reading register response: %w\", err)\n\t}\n\tif int64(len(data)) > maxResponseSize {\n\t\treturn nil, fmt.Errorf(\"register response exceeds max %d\", maxResponseSize)\n\t}\n\tvar resp tailcfg.RegisterResponse\n\tif err := json.Unmarshal(data, &resp); err != nil {\n\t\treturn nil, fmt.Errorf(\"decoding register response: %w\", err)\n\t}\n\tif resp.Error != \"\" {\n\t\treturn nil, fmt.Errorf(\"register: %s\", resp.Error)\n\t}\n\treturn &resp, nil\n}\n","sourceCodeStart":85,"sourceCodeEnd":117,"githubUrl":"https://github.com/tailscale/tailscale/blob/6e0912f97994f927632b34ae9e63b53d6516a6ac/control/tsp/register.go#L85-L117","documentation":"The 200 response body could not be read to completion: io.ReadAll over a LimitReader failed (control/tsp/register.go:103). The server accepted the request and began replying, then the stream broke: connection reset, TLS error, premature close, or context cancellation during the read.","triggerScenarios":"Server closes the connection mid-response; proxy/LB idle timeout cutting the transfer; ctx canceled while the body streams.","commonSituations":"Flaky links; aggressive intermediary timeouts; control server crashing while building the registration response.","solutions":["Retry the call; usually transient","If reproducible, correlate with server logs for a crash mid-response","Raise read timeouts on slow links and on intermediaries"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err != nil {\n    if errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, context.DeadlineExceeded) {\n        // mid-body break: safe to retry the whole request\n        resp, err = c.Register(ctx, opts)\n    }\n}","preventionTips":["Set generous read timeouts when responses can be large","Avoid cancelling the ctx while the response body is still streaming"],"tags":["go","tsp","network","io","response-body"],"backgroundTag":"network-read-failure","analyzedSha":"6e0912f97994f927632b34ae9e63b53d6516a6ac","analyzedAt":"2026-08-18T08:17:25.280Z","contentChangedAt":"2026-08-18T08:17:25.280Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}