{"record":{"id":"09dfa208bf200896","repo":"larksuite/cli","slug":"failed-to-read-tat-response-w","errorCode":null,"errorMessage":"failed to read TAT response: %w","messagePattern":"failed to read TAT response: %w","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/credential/tat_fetch.go","lineNumber":69,"sourceCode":"\tform.Set(\"grant_type\", \"client_credentials\")\n\tform.Set(\"client_id\", appID)\n\tform.Set(\"client_secret\", appSecret)\n\n\treq, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, strings.NewReader(form.Encode()))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treq.Header.Set(\"Content-Type\", \"application/x-www-form-urlencoded\")\n\n\tresp, err := httpClient.Do(req)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tdefer resp.Body.Close()\n\n\tbody, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20))\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to read TAT response: %w\", err)\n\t}\n\tif resp.StatusCode == http.StatusTooManyRequests {\n\t\tvar rateLimitErr *errs.APIError\n\t\tvar result tatResponse\n\t\tif json.Unmarshal(body, &result) == nil {\n\t\t\tdesc := result.ErrorDescription\n\t\t\tif desc == \"\" {\n\t\t\t\tdesc = result.Msg\n\t\t\t}\n\t\t\tclassified := classifyTATResponseCode(result.Code, result.Error, desc, string(brand), appID)\n\t\t\tvar apiErr *errs.APIError\n\t\t\tif errors.As(classified, &apiErr) &&\n\t\t\t\tapiErr.Subtype == errs.SubtypeRateLimit && apiErr.Retryable {\n\t\t\t\trateLimitErr = apiErr\n\t\t\t}\n\t\t}\n\n\t\tif rateLimitErr == nil {","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/credential/tat_fetch.go#L51-L87","documentation":"FetchTAT POSTs client credentials to the OAuth token endpoint to mint a tenant access token. After receiving the HTTP response, the body must be fully read (capped at 1 MiB via io.LimitReader); if that read fails the error is wrapped as \"failed to read TAT response\". This is a transport-level failure distinct from parse failures or credential rejections; it is returned raw (untyped) so probe callers treat it as transient noise and retryers can back off.","triggerScenarios":"io.ReadAll(io.LimitReader(resp.Body, 1<<20)) on the TAT endpoint response returns a non-nil error — the connection dropped mid-body, the server reset the connection, or a proxy/timeout interrupted the response stream after headers were received.","commonSituations":"Flaky network or VPN dropping long responses; gateway/proxy terminating the connection prematurely; server-side disruption returning truncated streams; aggressive client-side timeouts cutting the body read short.","solutions":["Retry the command — this is a transient transport failure, and transient TAT failures are intentionally untyped so retry/backoff applies","Check network stability to the accounts endpoint (VPN, corporate proxy, DNS) and retry","If it persists, verify the endpoint host is reachable with curl and check for gateway/proxy issues intercepting the OAuth token endpoint"],"exampleFix":"// typical caller-side retry\nconst maxAttempts = 3\nfor i := 0; i < maxAttempts; i++ {\n    tok, err := FetchTAT(ctx, hc, brand, appID, secret)\n    if err == nil {\n        return tok, nil\n    }\n    time.Sleep(time.Duration(1<<i) * time.Second) // backoff on transient read failures\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":"func isTransientTATError(err error) bool {\n\t// read/parse/5xx failures are untyped; typed errors are deterministic rejections\n\tvar apiErr *errs.APIError\n\treturn !errors.As(err, &apiErr)\n}","tryCatchPattern":"tok, err := credential.FetchTAT(ctx, hc, brand, appID, secret)\nif err != nil {\n\tvar apiErr *errs.APIError\n\tif !errors.As(err, &apiErr) { // untyped => transient (incl. body read failure)\n\t\ttok, err = retryWithBackoff(func() (string, error) {\n\t\t\treturn credential.FetchTAT(ctx, hc, brand, appID, secret)\n\t\t})\n\t}\n}","preventionTips":["Wrap token fetching in retry with exponential backoff and jitter","Set a generous context timeout so body reads are not cut off mid-stream","Avoid flaky proxies/VPN paths for the accounts endpoint; monitor gateway stability"],"tags":["network","http","oauth","transient"],"backgroundTag":"response-body-read-failed","analyzedSha":"7fd6ef3c07182257ce776cdc5a614e122d5bd4b3","analyzedAt":"2026-09-04T21:17:44.649Z","contentChangedAt":"2026-09-04T21:17:44.649Z","schemaVersion":2},"datasetVersion":"2026-09-12T02:17:10.037Z"}