{"record":{"id":"dc4713930e44b6bb","repo":"github/github-mcp-server","slug":"installation-token-request-failed-s-reading-res","errorCode":null,"errorMessage":"installation token request failed: %s (reading response: %w)","messagePattern":"installation token request failed: (.+?) \\(reading response: %w\\)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/githubapp/githubapp.go","lineNumber":156,"sourceCode":"\n\treq, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"creating installation token request: %w\", err)\n\t}\n\treq.Header.Set(\"Authorization\", \"Bearer \"+jwt)\n\treq.Header.Set(\"Accept\", \"application/vnd.github+json\")\n\treq.Header.Set(\"X-GitHub-Api-Version\", \"2022-11-28\")\n\n\tresp, err := s.httpClient.Do(req)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"requesting installation token: %w\", err)\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\n\tif resp.StatusCode != http.StatusCreated {\n\t\tsnippet, readErr := io.ReadAll(io.LimitReader(resp.Body, 512))\n\t\tif readErr != nil {\n\t\t\treturn nil, fmt.Errorf(\"installation token request failed: %s (reading response: %w)\", resp.Status, readErr)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"installation token request failed: %s: %s\", resp.Status, strings.TrimSpace(string(snippet)))\n\t}\n\n\tvar body struct {\n\t\tToken     string    `json:\"token\"`\n\t\tExpiresAt time.Time `json:\"expires_at\"`\n\t}\n\tif err := json.NewDecoder(resp.Body).Decode(&body); err != nil {\n\t\treturn nil, fmt.Errorf(\"decoding installation token response: %w\", err)\n\t}\n\tif body.Token == \"\" {\n\t\treturn nil, errors.New(\"installation token response did not contain a token\")\n\t}\n\tif body.ExpiresAt.IsZero() {\n\t\treturn nil, errors.New(\"installation token response did not contain an expiry\")\n\t}\n\treturn &oauth2.Token{","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/github/github-mcp-server/blob/0ea1f775a7c73eff1bd2e25904d01136756bbfe2/internal/githubapp/githubapp.go#L138-L174","documentation":"The token endpoint answered with a non-201 status AND reading (up to 512 bytes of) the error body failed — e.g. the connection reset mid-body. The message includes resp.Status and the read error so the status code is never lost. This is the rare degraded branch of the non-201 handler at internal/githubapp/githubapp.go:153-157; the common branch is error 106.","triggerScenarios":"GitHub's server (or an intercepting proxy) sends a 4xx/5xx status line, then resets or half-closes the TCP connection before the body arrives, so io.ReadAll(io.LimitReader(resp.Body, 512)) at internal/githubapp/githubapp.go:154 returns a read error. Common with misbehaving transparent proxies and LB idle-timeout kills.","commonSituations":"Corporate MITM proxy that truncates error responses; a GHES node behind an aggressive load balancer; flaky NAT gateways on cellular/VPN links; connections killed by security appliances after sending status.","solutions":["Read the status code from the message — it usually identifies the real problem even without a body","Reproduce with curl from the same host to see the full response the proxy/host returns","Bypass or fix the intercepting proxy for api.github.com traffic","Retry — mid-body resets are typically transient"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if strings.Contains(err.Error(), \"installation token request failed:\") {\n    // status is in the message even when the body read failed; classify on it\n}","preventionTips":["Bypass MITM proxies for api.github.com so error bodies arrive intact","Treat mid-body resets as transient: the next AccessToken() call retries the whole request"],"tags":["network","proxy","http","truncated-response","rare"],"backgroundTag":null,"analyzedSha":"0ea1f775a7c73eff1bd2e25904d01136756bbfe2","analyzedAt":"2026-08-15T18:10:19.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}