{"record":{"id":"f47cf922384a9380","repo":"kubernetes/kops","slug":"reading-instance-metadata-response-w","errorCode":null,"errorMessage":"reading instance metadata response: %w","messagePattern":"reading instance metadata response: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upup/pkg/fi/cloudup/linode/linodemetadata/authenticator.go","lineNumber":120,"sourceCode":"\tinstanceReq, err := http.NewRequestWithContext(ctx, http.MethodGet, metadataBaseURL+\"/v1/instance\", nil)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"building instance metadata request: %w\", err)\n\t}\n\tinstanceReq.Header.Set(\"Metadata-Token\", token)\n\n\tinstanceResp, err := client.Do(instanceReq)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"fetching instance metadata: %w\", err)\n\t}\n\tdefer instanceResp.Body.Close()\n\n\tif instanceResp.StatusCode != http.StatusOK {\n\t\treturn \"\", fmt.Errorf(\"fetching instance metadata: unexpected status code %d\", instanceResp.StatusCode)\n\t}\n\n\tinstanceBytes, err := io.ReadAll(instanceResp.Body)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"reading instance metadata response: %w\", err)\n\t}\n\n\tvalue := parseLinodeMetadataValue(string(instanceBytes), key)\n\tif value == \"\" {\n\t\treturn \"\", fmt.Errorf(\"instance %s from Akamai (Linode) metadata was empty\", key)\n\t}\n\treturn value, nil\n}\n\n// parseLinodeMetadataValue parses the Akamai (Linode) metadata response for the given key\n// and returns the value as a string.\nfunc parseLinodeMetadataValue(metadata string, key string) string {\n\tprefix := key + \":\"\n\tfor _, line := range strings.Split(metadata, \"\\n\") {\n\t\tline = strings.TrimSpace(line)\n\t\tif !strings.HasPrefix(line, prefix) {\n\t\t\tcontinue\n\t\t}","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/cloudup/linode/linodemetadata/authenticator.go#L102-L138","documentation":"getLinodeMetadataValue fetches instance metadata from the Akamai (Linode) metadata service. After the HTTP request succeeds with status 200, it reads the response body with io.ReadAll; if that read fails (connection reset mid-body, truncated chunked transfer, context cancellation), the error is wrapped with this message. It signals the metadata HTTP exchange broke while downloading the payload, not that the payload was invalid.","triggerScenarios":"io.ReadAll(instanceResp.Body) returns a non-nil err after a 200 status: the metadata server closes the connection mid-response, the chunked body is truncated, a request deadline/context expires during the read, or a proxy intercepts and drops the stream.","commonSituations":"Flaky network or overloaded metadata endpoint on a Linode host; a local test httptest server that closes the connection without writing a full body; TLS interception proxies killing long-lived keep-alive connections; callers cancelling the request context mid-read.","solutions":["Inspect the wrapped error (%w) to identify the root cause (connection reset, context deadline, etc.)","Retry the metadata request; body-read failures are typically transient","Check for proxies or middleboxes between the host and the metadata endpoint","Increase or verify the HTTP client timeout / context deadline is not too tight","Run the request against a known-good metadata endpoint to rule out server-side truncation"],"exampleFix":"// before\ninstanceBytes, err := io.ReadAll(instanceResp.Body)\nif err != nil {\n\treturn \"\", fmt.Errorf(\"reading instance metadata response: %w\", err)\n}\n// after\ninstanceBytes, err := io.ReadAll(io.LimitReader(instanceResp.Body, maxMetadataSize))\nif err != nil {\n\treturn \"\", fmt.Errorf(\"reading instance metadata response: %w\", err)\n}","handlingStrategy":"retry","validationCode":"if instanceResp.Body == nil { return errors.New(\"metadata response has no body\") }","typeGuard":null,"tryCatchPattern":"bytes, err := io.ReadAll(resp.Body)\nif err != nil {\n\treturn retryable(fmt.Errorf(\"reading instance metadata response: %w\", err))\n}","preventionTips":["Set a sane client timeout and retry idempotent metadata GETs","Wrap the body in io.LimitReader to avoid unbounded reads","Log the wrapped cause to distinguish transient resets from deadline expiry","Treat body-read errors as retryable, unlike parse errors"],"tags":["http","io","linode","metadata"],"backgroundTag":"http-body-read-failed","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}