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

  1. Retry the command — this is a transient transport failure, and transient TAT failures are intentionally untyped so retry/backoff applies
  2. Check network stability to the accounts endpoint (VPN, corporate proxy, DNS) and retry
  3. 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

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


AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/09dfa208bf200896. Report an issue: GitHub.