larksuite/cli · error
failed to read TAT response: %w
Error message
failed to read TAT response: %w
What it means
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.
Source
Thrown at internal/credential/tat_fetch.go:69
form.Set("grant_type", "client_credentials")
form.Set("client_id", appID)
form.Set("client_secret", appSecret)
req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, strings.NewReader(form.Encode()))
if err != nil {
return "", err
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, err := httpClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
if err != nil {
return "", fmt.Errorf("failed to read TAT response: %w", err)
}
if resp.StatusCode == http.StatusTooManyRequests {
var rateLimitErr *errs.APIError
var result tatResponse
if json.Unmarshal(body, &result) == nil {
desc := result.ErrorDescription
if desc == "" {
desc = result.Msg
}
classified := classifyTATResponseCode(result.Code, result.Error, desc, string(brand), appID)
var apiErr *errs.APIError
if errors.As(classified, &apiErr) &&
apiErr.Subtype == errs.SubtypeRateLimit && apiErr.Retryable {
rateLimitErr = apiErr
}
}
if rateLimitErr == nil {View on GitHub (pinned to 7fd6ef3c07)
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
Example fix
// typical caller-side retry
const maxAttempts = 3
for i := 0; i < maxAttempts; i++ {
tok, err := FetchTAT(ctx, hc, brand, appID, secret)
if err == nil {
return tok, nil
}
time.Sleep(time.Duration(1<<i) * time.Second) // backoff on transient read failures
} Defensive patterns
Strategy: retry
Type guard
func isTransientTATError(err error) bool {
// read/parse/5xx failures are untyped; typed errors are deterministic rejections
var apiErr *errs.APIError
return !errors.As(err, &apiErr)
} Try / catch
tok, err := credential.FetchTAT(ctx, hc, brand, appID, secret)
if err != nil {
var apiErr *errs.APIError
if !errors.As(err, &apiErr) { // untyped => transient (incl. body read failure)
tok, err = retryWithBackoff(func() (string, error) {
return credential.FetchTAT(ctx, hc, brand, appID, secret)
})
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- app registration failed: HTTP %d – response not JSON
- app registration failed: %s
- Device authorization failed: read body: %v
- Device authorization failed: %s
- failed to read response body in security transport: %w
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/09dfa208bf200896.
Report an issue: GitHub.