flipped-aurora/gin-vue-admin · error

解析大模型响应失败: status=%d content-type=%s body=%s err=%w

Error message

解析大模型响应失败: status=%d content-type=%s body=%s err=%w

What it means

Even with a 2xx status, LLMAuto json.Unmarshals the body into commonResp.Response; if the body is not valid JSON or its shape doesn't fit, this error reports status, content-type, body preview, and the unmarshal error. It catches non-JSON or unexpected response formats from the upstream.

Source

Thrown at server/service/system/auto_code_llm.go:50

	if err != nil {
		return nil, fmt.Errorf("调用上游大模型服务失败: %w", err)
	}
	defer res.Body.Close()

	body, err := io.ReadAll(res.Body)
	if err != nil {
		return nil, fmt.Errorf("读取大模型响应失败: %w", err)
	}

	bodyPreview := previewResponseBody(body)
	contentType := res.Header.Get("Content-Type")
	if res.StatusCode < 200 || res.StatusCode >= 300 {
		return nil, fmt.Errorf("上游大模型服务返回非 2xx: status=%d content-type=%s body=%s", res.StatusCode, contentType, bodyPreview)
	}

	var resStruct commonResp.Response
	if err = json.Unmarshal(body, &resStruct); err != nil {
		return nil, fmt.Errorf("解析大模型响应失败: status=%d content-type=%s body=%s err=%w", res.StatusCode, contentType, bodyPreview, err)
	}

	if resStruct.Code != commonResp.SUCCESS {
		return nil, fmt.Errorf("大模型服务返回业务错误: code=%d msg=%s body=%s", resStruct.Code, resStruct.Msg, bodyPreview)
	}

	return resStruct.Data, nil
}

func (s *AutoCodeService) LLMAutoStream(ctx context.Context, llm common.JSONMap) (*http.Response, error) {
	path, err := buildLLMAutoPath(llm)
	if err != nil {
		return nil, err
	}

	payload := cloneLLMAutoJSONMap(llm)
	responseMode := strings.ToLower(strings.TrimSpace(fmt.Sprintf("%v", payload["response_mode"])))
	if responseMode == "" {

View on GitHub (pinned to 3136500ef3)

Solutions

  1. Check the body preview and Content-Type in the error — if it's HTML, the URL is wrong (proxy/login page)
  2. If it's text/event-stream or partial data, use LLMAutoStream instead of LLMAuto
  3. Compare the actual JSON envelope with commonResp.Response fields (code/data/msg) and adapt config or parsing
  4. Verify response_mode/headers in the llm config match what the provider expects for non-streaming calls

Example fix

// before: blind unmarshal into fixed envelope
var resStruct commonResp.Response
if err = json.Unmarshal(body, &resStruct); err != nil { return nil, ... }
// after: guard on content type first
if strings.Contains(contentType, "text/event-stream") {
    return nil, fmt.Errorf("上游返回流式响应,请使用流式接口")
}
var resStruct commonResp.Response
if err = json.Unmarshal(body, &resStruct); err != nil { return nil, ... }
Defensive patterns

Strategy: type-guard

Validate before calling

ct := res.Header.Get("Content-Type")
if !strings.Contains(ct, "application/json") {
    return fmt.Errorf("期望 JSON 响应,实际 Content-Type: %s", ct)
}
if !json.Valid(body) {
    return fmt.Errorf("响应不是合法 JSON: %s", previewResponseBody(body))
}

Type guard

func isLLMEnvelope(b []byte) bool {
    var probe struct {
        Code interface{} `json:"code"`
    }
    return json.Unmarshal(b, &probe) == nil
}

Try / catch

data, err := svc.LLMAuto(ctx, llmCfg)
if err != nil {
    if strings.Contains(err.Error(), "解析大模型响应失败") {
        // fall back to streaming endpoint or return contract-mismatch diagnostics
    }
    return err
}

Prevention

When it happens

Trigger: The upstream returns 2xx but the body cannot be unmarshaled: HTML error/login pages behind a mis-URL'd gateway, plain-text responses, SSE/partial data returned to a non-stream call, or a JSON shape with different field types (e.g. code as string).

Common situations: Pointing the LLM URL at a proxy/landing page, provider returning streaming (text/event-stream) when response_mode wasn't set correctly, provider SDK contract change, Content-Type application/json but vendor-specific envelope.

Related errors


AI-assisted analysis of flipped-aurora/gin-vue-admin@3136500ef3 (2026-08-31). Data as JSON: /api/errors/81b97918c4a90d5b. Report an issue: GitHub.