{"record":{"id":"d31ea22a7be91629","repo":"larksuite/cli","slug":"tat-response-missing-access-token-http-d","errorCode":null,"errorMessage":"TAT response missing access_token (HTTP %d)","messagePattern":"TAT response missing access_token \\(HTTP (.+?)\\)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/credential/tat_fetch.go","lineNumber":126,"sourceCode":"\tif result.Code == 0 && result.AccessToken != \"\" {\n\t\treturn result.AccessToken, nil\n\t}\n\n\t// Transient/server-side failures stay untyped so probe callers stay silent and\n\t// retryers can back off; only deterministic client rejections are typed. Covers\n\t// 5xx and the OAuth transient error strings (server_error,\n\t// temporarily_unavailable, slow_down). HTTP 429 was already returned above\n\t// as a typed rate-limit error with retry guidance and an upstream delay when available.\n\tif resp.StatusCode >= 500 ||\n\t\tresult.Error == \"server_error\" || result.Error == \"temporarily_unavailable\" ||\n\t\tresult.Error == \"slow_down\" {\n\t\treturn \"\", fmt.Errorf(\"TAT endpoint transient failure (HTTP %d, code=%d, error=%q): %s\",\n\t\t\tresp.StatusCode, result.Code, result.Error, result.ErrorDescription)\n\t}\n\n\t// A 2xx with neither token nor error is a malformed success — ambiguous, untyped.\n\tif result.Code == 0 && result.Error == \"\" {\n\t\treturn \"\", fmt.Errorf(\"TAT response missing access_token (HTTP %d)\", resp.StatusCode)\n\t}\n\n\t// Prefer the OAuth error_description; fall back to the legacy Lark `msg` so a\n\t// gateway-level {code, msg} response (carrying no OAuth fields) still yields a\n\t// non-empty typed message instead of a bare \"API error: [code]\".\n\tdesc := result.ErrorDescription\n\tif desc == \"\" {\n\t\tdesc = result.Msg\n\t}\n\treturn \"\", classifyTATResponseCode(result.Code, result.Error, desc, string(brand), appID)\n}\n\nfunc tatRetryAfterSeconds(header http.Header) int {\n\tfor _, name := range []string{\"X-Ogw-Ratelimit-Reset\", \"Retry-After\"} {\n\t\tseconds, err := strconv.Atoi(strings.TrimSpace(header.Get(name)))\n\t\tif err == nil && seconds > 0 {\n\t\t\treturn seconds\n\t\t}","sourceCodeStart":108,"sourceCodeEnd":144,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/credential/tat_fetch.go#L108-L144","documentation":"The TAT endpoint returned a 2xx response whose parsed JSON contains neither an access_token nor an OAuth error field (code==0 and error==\"\") — a malformed success. The library treats it as ambiguous/untyped because the HTTP status says success but no token was delivered.","triggerScenarios":"A 2xx response with {code:0} but no access_token field, or an empty/other-shaped JSON body that unmarshals into zero fields, from the token endpoint.","commonSituations":"Upstream schema change on the token endpoint (fields renamed/removed); a misconfigured endpoint (e.g. wrong path) returning a benign empty JSON success; an intermediary caching or rewriting the response body.","solutions":["Log the full 2xx response body and headers to identify what actually came back instead of an access_token.","Verify you are calling the current, documented TAT/OAuth token endpoint path and version.","Retry once in case of an intermittent upstream issue; if reproducible, report the malformed response shape to the API provider.","Check whether a proxy or response-rewriting middleware is stripping or altering the JSON payload."],"exampleFix":"// before (assumes token present whenever err==nil)\n// after (fail fast and dump body when token is missing)\nif resp.StatusCode == 200 && !strings.Contains(string(body), \"access_token\") {\n    log.Fatalf(\"unexpected 2xx TAT body: %s\", string(body))\n}","handlingStrategy":"fallback","validationCode":null,"typeGuard":"func isMalformedSuccess(err error) bool {\n    return strings.Contains(err.Error(), \"TAT response missing access_token\")\n}","tryCatchPattern":"token, err := credential.FetchTAT(ctx, cfg)\nif err != nil {\n    if strings.Contains(err.Error(), \"TAT response missing access_token\") {\n        // HTTP said 2xx but no token: capture body, retry once, then escalate\n        log.Printf(\"malformed TAT success, retrying once\")\n        token, err = credential.FetchTAT(ctx, cfg)\n    }\n    if err != nil { return err }\n}","preventionTips":["Pin to current API endpoint paths and upgrade SDK/metadata when the provider changes token response shapes.","Log full response bodies on this error to enable upstream bug reports.","Check middleware/proxies that might strip or rewrite JSON response fields.","Keep a regression test decoding the real token endpoint response shape in CI against a recorded fixture."],"tags":["malformed-response","http","token-fetch","schema-change"],"backgroundTag":"malformed-success-response","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"}