larksuite/cli · error

response parse error: %w (body: %s)

Error message

response parse error: %w (body: %s)

What it means

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).

Source

Thrown at internal/client/response.go:199

	}
	return errs.NewInternalError(errs.SubtypeFileIO, "save response: %v", err).WithCause(err)
}

// ── JSON helpers ──

// IsJSONContentType reports whether the Content-Type header indicates a JSON response.
func IsJSONContentType(ct string) bool {
	return strings.Contains(ct, "application/json") || strings.Contains(ct, "text/json")
}

// ParseJSONResponse decodes a raw SDK response body as JSON.
// CallAPI and HandleResponse both delegate to this function.
func ParseJSONResponse(resp *larkcore.ApiResp) (interface{}, error) {
	var result interface{}
	dec := json.NewDecoder(bytes.NewReader(resp.RawBody))
	dec.UseNumber()
	if err := dec.Decode(&result); err != nil {
		return nil, fmt.Errorf("response parse error: %w (body: %s)", err, util.TruncateStr(string(resp.RawBody), 500))
	}
	return result, nil
}

// ── File saving ──

// SaveResponse writes an API response body to the given outputPath and returns metadata.
// It delegates to FileIO.Save for path validation and atomic write; fio must not be nil.
func SaveResponse(fio fileio.FileIO, resp *larkcore.ApiResp, outputPath string) (map[string]interface{}, error) {
	result, err := fio.Save(outputPath, fileio.SaveOptions{
		ContentType:   resp.Header.Get("Content-Type"),
		ContentLength: int64(len(resp.RawBody)),
	}, bytes.NewReader(resp.RawBody))
	if err != nil {
		var me *fileio.MkdirError
		var we *fileio.WriteError
		switch {
		case errors.Is(err, fileio.ErrPathValidation):

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Inspect the truncated body in the error message to identify what the server returned
  2. Check for proxy/WAF interception and retry with a direct network path
  3. Retry the request if it was transient (502/504 gateway pages)
  4. Verify API endpoint and auth are correct — non-JSON often means an error page
Defensive patterns

Strategy: try-catch

Validate before calling

if !json.Valid(resp.RawBody) {
    return fmt.Errorf("non-JSON response (status %d): %.200s", resp.StatusCode, resp.RawBody)
}

Try / catch

result, err := ParseJSONResponse(resp)
if err != nil {
    var pe *ParseError
    if strings.Contains(err.Error(), "response parse error") {
        log.Printf("API returned non-JSON body; inspecting truncated payload: %v", err)
        // inspect body in message; retry or fall back to raw text
        return retryWithBackoff(req)
    }
    return err
}

Prevention

When it happens

Trigger: 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.

Common situations: Corporate proxies or captive portals injecting HTML; rate-limit/gateway pages; server 200 with empty body; SDK version changes altering expected response shape.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


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