{"record":{"id":"c98e47338165d478","repo":"larksuite/cli","slug":"response-parse-error-w-body-s","errorCode":null,"errorMessage":"response parse error: %w (body: %s)","messagePattern":"response parse error: %w \\(body: (.+?)\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/client/response.go","lineNumber":199,"sourceCode":"\t}\n\treturn errs.NewInternalError(errs.SubtypeFileIO, \"save response: %v\", err).WithCause(err)\n}\n\n// ── JSON helpers ──\n\n// IsJSONContentType reports whether the Content-Type header indicates a JSON response.\nfunc IsJSONContentType(ct string) bool {\n\treturn strings.Contains(ct, \"application/json\") || strings.Contains(ct, \"text/json\")\n}\n\n// ParseJSONResponse decodes a raw SDK response body as JSON.\n// CallAPI and HandleResponse both delegate to this function.\nfunc ParseJSONResponse(resp *larkcore.ApiResp) (interface{}, error) {\n\tvar result interface{}\n\tdec := json.NewDecoder(bytes.NewReader(resp.RawBody))\n\tdec.UseNumber()\n\tif err := dec.Decode(&result); err != nil {\n\t\treturn nil, fmt.Errorf(\"response parse error: %w (body: %s)\", err, util.TruncateStr(string(resp.RawBody), 500))\n\t}\n\treturn result, nil\n}\n\n// ── File saving ──\n\n// SaveResponse writes an API response body to the given outputPath and returns metadata.\n// It delegates to FileIO.Save for path validation and atomic write; fio must not be nil.\nfunc SaveResponse(fio fileio.FileIO, resp *larkcore.ApiResp, outputPath string) (map[string]interface{}, error) {\n\tresult, err := fio.Save(outputPath, fileio.SaveOptions{\n\t\tContentType:   resp.Header.Get(\"Content-Type\"),\n\t\tContentLength: int64(len(resp.RawBody)),\n\t}, bytes.NewReader(resp.RawBody))\n\tif err != nil {\n\t\tvar me *fileio.MkdirError\n\t\tvar we *fileio.WriteError\n\t\tswitch {\n\t\tcase errors.Is(err, fileio.ErrPathValidation):","sourceCodeStart":181,"sourceCodeEnd":217,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/client/response.go#L181-L217","documentation":"ParseJSONResponse decodes the API response RawBody with json.Decoder (UseNumber) and wraps a decode failure with the underlying error plus a 500-char truncated body. It exists so callers see both why parsing failed and what the server actually returned (often an HTML error page or empty/garbled body).","triggerScenarios":"Lark API (or an intermediary) returning non-JSON bodies: HTML error pages from proxies/WAFs, empty responses (EOF), truncated responses, or wrong Content-Type payloads.","commonSituations":"Corporate proxies or captive portals injecting HTML; rate-limit/gateway pages; server 200 with empty body; SDK version changes altering expected response shape.","solutions":["Inspect the truncated body in the error message to identify what the server returned","Check for proxy/WAF interception and retry with a direct network path","Retry the request if it was transient (502/504 gateway pages)","Verify API endpoint and auth are correct — non-JSON often means an error page"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if !json.Valid(resp.RawBody) {\n    return fmt.Errorf(\"non-JSON response (status %d): %.200s\", resp.StatusCode, resp.RawBody)\n}","typeGuard":null,"tryCatchPattern":"result, err := ParseJSONResponse(resp)\nif err != nil {\n    var pe *ParseError\n    if strings.Contains(err.Error(), \"response parse error\") {\n        log.Printf(\"API returned non-JSON body; inspecting truncated payload: %v\", err)\n        // inspect body in message; retry or fall back to raw text\n        return retryWithBackoff(req)\n    }\n    return err\n}","preventionTips":["Check status codes before parsing; gateways often return HTML for 5xx","Detect and bypass captive portals / corporate proxies in CI","Log RawBody (truncated) on any parse failure for forensics","Keep SDK and endpoint versions in sync"],"tags":["json","http","parsing","network"],"backgroundTag":"json-parse-error","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"}