{"record":{"id":"3a373a9f7d28c261","repo":"jeessy2/ddns-go","slug":"api-d-s","errorCode":null,"errorMessage":"API请求失败，状态码: %d, 响应: %s","messagePattern":"API请求失败，状态码: (.+?), 响应: (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"dns/eranet.go","lineNumber":284,"sourceCode":"\treq.Header.Set(\"Accept\", \"application/json\")\n\n\t// 发送请求\n\tclient := t.httpClient\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"请求失败: %v\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\t// 读取响应\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"读取响应失败: %v\", err)\n\t}\n\n\t// 检查HTTP状态码\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"API请求失败，状态码: %d, 响应: %s\", resp.StatusCode, string(body))\n\t}\n\n\treturn body, nil\n}\n","sourceCodeStart":266,"sourceCodeEnd":289,"githubUrl":"https://github.com/jeessy2/ddns-go/blob/5874c2e6667c69357ab95079421131104bd3fcae/dns/eranet.go#L266-L289","documentation":"Thrown by Eranet.request when the eranet.com API returns an HTTP status code other than 200. The error includes the numeric status and the raw response body, so the API's own error payload (often JSON explaining auth/signature problems) is embedded in the message. Affects create, modify, and getRecordList.","triggerScenarios":"Any non-200 response from https://www.eranet.com: 401/403 for bad API credentials, 4xx for invalid parameters or signature mismatch, 5xx for eranet server errors, or 404 from a wrong apiPath.","commonSituations":"Wrong or expired eranet API key/secret in config; system clock skew breaking the HMAC Signature; parameter values rejected by the API (invalid domain/record); eranet service outage returning 5xx.","solutions":["Read the embedded response body in the error message — it usually states the API-level cause","Verify the API key/secret configured for the eranet provider are correct and active","Check system time (NTP) — the request Signature depends on timestamps and fails on skew","If status is 5xx, retry later — it is an eranet-side outage","Compare parameter values (domain, subdomain, record type) against eranet API docs"],"exampleFix":"// before\n// opaque handling of error\nreturn err\n// after\nvar apiErr struct {\n    Code int    `json:\"code\"`\n    Msg  string `json:\"message\"`\n}\nif json.Unmarshal(body, &apiErr) == nil && apiErr.Msg != \"\" {\n    return fmt.Errorf(\"API请求失败，状态码: %d, 原因: %s\", resp.StatusCode, apiErr.Msg)\n}","handlingStrategy":"try-catch","validationCode":"// validate credentials & params before calling the API\nif apiKey == \"\" || apiSecret == \"\" {\n    return fmt.Errorf(\"eranet credentials missing\")\n}\nif strings.TrimSpace(domainName) == \"\" {\n    return fmt.Errorf(\"domain name required\")\n}","typeGuard":"func isEranetStatusError(err error) (statusCode int, body string, ok bool) {\n    // \"API请求失败，状态码: %d, 响应: %s\"\n    const prefix = \"API请求失败，状态码: \"\n    msg := err.Error()\n    i := strings.Index(msg, prefix)\n    if i < 0 {\n        return 0, \"\", false\n    }\n    rest := msg[i+len(prefix):]\n    j := strings.Index(rest, \", 响应: \")\n    if j < 0 {\n        return 0, \"\", false\n    }\n    code, e := strconv.Atoi(strings.TrimSpace(rest[:j]))\n    if e != nil {\n        return 0, \"\", false\n    }\n    return code, rest[j+len(\", 响应: \"):], true\n}","tryCatchPattern":"_, err := eranetClient.GetRecordList(domain)\nif err != nil {\n    if code, body, ok := isEranetStatusError(err); ok {\n        switch {\n        case code == 401 || code == 403:\n            return fmt.Errorf(\"check eranet API credentials: %s\", body)\n        case code >= 500:\n            return retryWithBackoff(3, func() error { _, err = eranetClient.GetRecordList(domain); return err })\n        default:\n            return fmt.Errorf(\"eranet rejected request (%d): %s\", code, body)\n        }\n    }\n    return err\n}","preventionTips":["Always log the full error — the API response body inside it names the real cause","Verify API key/secret in config and rotate expired tokens","Keep the host clock NTP-synced (HMAC Signature validity depends on it)","Cross-check domain/subdomain/record-type values against eranet API docs","Treat 5xx as retryable and 4xx as configuration bugs"],"tags":["http","api-error","status-code","go"],"backgroundTag":"http-non-200-response","analyzedSha":"5874c2e6667c69357ab95079421131104bd3fcae","analyzedAt":"2026-09-03T15:41:16.799Z","contentChangedAt":"2026-09-03T15:41:16.799Z","schemaVersion":2},"datasetVersion":"2026-09-11T00:17:11.886Z"}