{"record":{"id":"b79979dc6f00b417","repo":"Tencent/WeKnora","slug":"zhipu-api-returned-status-d-s-s","errorCode":null,"errorMessage":"Zhipu API returned status %d (%s): %s","messagePattern":"Zhipu API returned status (.+?) \\((.+?)\\): (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/infrastructure/web_search/zhipu.go","lineNumber":231,"sourceCode":"\t}\n\treturn time.Time{}, false\n}\n\nfunc readZhipuResponseBody(reader io.Reader) ([]byte, error) {\n\tbody, err := io.ReadAll(io.LimitReader(reader, maxZhipuResponseBytes+1))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to read Zhipu response: %w\", err)\n\t}\n\tif len(body) > maxZhipuResponseBytes {\n\t\treturn nil, fmt.Errorf(\"Zhipu response exceeds %d bytes\", maxZhipuResponseBytes)\n\t}\n\treturn body, nil\n}\n\nfunc zhipuHTTPError(statusCode int, body []byte) error {\n\tvar response zhipuSearchResponse\n\tif err := json.Unmarshal(body, &response); err == nil && (response.Error.Code != \"\" || response.Error.Message != \"\") {\n\t\treturn fmt.Errorf(\"Zhipu API returned status %d (%s): %s\", statusCode, response.Error.Code, response.Error.Message)\n\t}\n\tdetail := strings.TrimSpace(string(body))\n\tif len(detail) > 4096 {\n\t\tdetail = detail[:4096]\n\t}\n\tif detail == \"\" {\n\t\treturn fmt.Errorf(\"Zhipu API returned status %d\", statusCode)\n\t}\n\treturn fmt.Errorf(\"Zhipu API returned status %d: %s\", statusCode, detail)\n}\n\ntype zhipuSearchRequest struct {\n\tSearchQuery  string `json:\"search_query\"`\n\tSearchEngine string `json:\"search_engine\"`\n\tSearchIntent bool   `json:\"search_intent\"`\n\tCount        int    `json:\"count\"`\n\tContentSize  string `json:\"content_size\"`\n}","sourceCodeStart":213,"sourceCodeEnd":249,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/infrastructure/web_search/zhipu.go#L213-L249","documentation":"When the Zhipu API returns a non-200 status, zhipuHTTPError first tries to decode a structured error object from the body; if present it reports status, error code, and message in one formatted error. This is the structured variant of the non-200 handling path.","triggerScenarios":"Calling Search() when Zhipu returns 4xx/5xx AND the body contains a parseable {error:{code,message}} object.","commonSituations":"401/403 from an invalid or expired API key; 429 rate limiting; 400 from invalid search_engine or query parameters.","solutions":["Read the embedded error code: 401/403 → fix API key; 429 → back off and retry; 4xx → fix request params","Confirm the Authorization header carries a valid Bearer token (p.apiKey is set)","Check Zhipu service status if 5xx persists","Implement retry-with-backoff for 429/5xx only"],"exampleFix":"// before\nif err != nil { log.Println(err); os.Exit(1) } // opaque\n// after\nif err != nil {\n    var apiErr *ZhipuStatusError\n    if errors.As(err, &apiErr) && apiErr.StatusCode == http.StatusTooManyRequests {\n        time.Sleep(backoff); return retry(ctx)\n    }\n    return err\n}","handlingStrategy":"try-catch","validationCode":"if !strings.HasPrefix(apiURL, \"https://\") { return errors.New(\"zhipu endpoint must be https\") }\nif apiKey == \"\" { return errors.New(\"zhipu api key required\") }","typeGuard":"func statusCodeOf(err error) (int, bool) {\n    m := regexp.MustCompile(`status (\\d{3})`).FindStringSubmatch(err.Error())\n    if m == nil { return 0, false }\n    c, _ := strconv.Atoi(m[1]); return c, true\n}","tryCatchPattern":"results, err := provider.Search(ctx, q)\nif err != nil {\n    if sc, ok := statusCodeOf(err); ok && (sc == 429 || sc >= 500) { return retryWithBackoff(ctx, q) }\n    return err\n}","preventionTips":["Retry only 429/5xx; never retry 401/403/400","Keep the API key fresh and monitor quota usage","Log the structured code+message from the error for triage","Set up alerts on repeated non-200s from the provider"],"tags":["http-status","api-error","auth","zhipu"],"backgroundTag":"http-4xx-error","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}