{"record":{"id":"c46eead0b5fd94e6","repo":"flipped-aurora/gin-vue-admin","slug":"2xx-status-d-content-type-s-body-s","errorCode":null,"errorMessage":"上游大模型服务返回非 2xx: status=%d content-type=%s body=%s","messagePattern":"上游大模型服务返回非 2xx: status=(.+?) content-type=(.+?) body=(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/service/system/auto_code_llm.go","lineNumber":45,"sourceCode":"\t\thttp.MethodPost,\n\t\tnil,\n\t\tnil,\n\t\tllm,\n\t)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"调用上游大模型服务失败: %w\", err)\n\t}\n\tdefer res.Body.Close()\n\n\tbody, err := io.ReadAll(res.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"读取大模型响应失败: %w\", err)\n\t}\n\n\tbodyPreview := previewResponseBody(body)\n\tcontentType := res.Header.Get(\"Content-Type\")\n\tif res.StatusCode < 200 || res.StatusCode >= 300 {\n\t\treturn nil, fmt.Errorf(\"上游大模型服务返回非 2xx: status=%d content-type=%s body=%s\", res.StatusCode, contentType, bodyPreview)\n\t}\n\n\tvar resStruct commonResp.Response\n\tif err = json.Unmarshal(body, &resStruct); err != nil {\n\t\treturn nil, fmt.Errorf(\"解析大模型响应失败: status=%d content-type=%s body=%s err=%w\", res.StatusCode, contentType, bodyPreview, err)\n\t}\n\n\tif resStruct.Code != commonResp.SUCCESS {\n\t\treturn nil, fmt.Errorf(\"大模型服务返回业务错误: code=%d msg=%s body=%s\", resStruct.Code, resStruct.Msg, bodyPreview)\n\t}\n\n\treturn resStruct.Data, nil\n}\n\nfunc (s *AutoCodeService) LLMAutoStream(ctx context.Context, llm common.JSONMap) (*http.Response, error) {\n\tpath, err := buildLLMAutoPath(llm)\n\tif err != nil {\n\t\treturn nil, err","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/flipped-aurora/gin-vue-admin/blob/3136500ef380842b0eb6c4daa87c3f8a47fcf9e0/server/service/system/auto_code_llm.go#L27-L63","documentation":"After reading the body, LLMAuto checks the HTTP status and rejects anything outside 200–299, embedding status, Content-Type, and a truncated body preview. The upstream LLM service answered, but with an HTTP-level failure (auth, rate limit, bad request, server error).","triggerScenarios":"Calling LLMAuto when the upstream returns 4xx/5xx — e.g. 401 for a bad/expired API key, 429 rate limit, 400 for an invalid model name or malformed payload, 502/503 for upstream outages.","commonSituations":"Rotated or unset API keys, exhausted provider quota, wrong model identifier after a provider version change, payload fields not accepted by the provider, provider regional outage.","solutions":["Read the status and body preview in the error to identify the upstream cause (401→key, 429→quota, 400→payload)","Fix the API key / model name / request payload in the llm config accordingly","For 429, add backoff/retry respecting the provider's rate limits","For 5xx, retry later or switch to a fallback provider/region"],"exampleFix":"// before: only generic wrap upstream, no status inspection\nif err != nil { return nil, err }\n// after: inspect status before treating response as success\nif res.StatusCode == http.StatusUnauthorized {\n    return nil, fmt.Errorf(\"LLM 鉴权失败，请检查 API Key\")\n}\nif res.StatusCode >= 500 {\n    return nil, fmt.Errorf(\"LLM 服务端错误，可稍后重试: status=%d\", res.StatusCode)\n}","handlingStrategy":"validation","validationCode":"if res.StatusCode < 200 || res.StatusCode >= 300 {\n    return fmt.Errorf(\"上游返回非 2xx: status=%d body=%s\", res.StatusCode, previewResponseBody(body))\n}","typeGuard":null,"tryCatchPattern":"if err != nil {\n    if strings.Contains(err.Error(), \"status=429\") {\n        // backoff and retry respecting rate limit\n    } else if strings.Contains(err.Error(), \"status=401\") {\n        // refresh API key / surface auth config error to operator\n    }\n    return err\n}","preventionTips":["Keep API keys and model names in validated config; check for emptiness before calling","Monitor provider quota to anticipate 429s","Pin model identifiers and update them when the provider changes versions","Log status + body preview on failure for fast triage"],"tags":["http","llm","api","status-code"],"backgroundTag":"http-non-2xx-response","analyzedSha":"3136500ef380842b0eb6c4daa87c3f8a47fcf9e0","analyzedAt":"2026-08-31T13:50:02.721Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}