{"record":{"id":"0897afb81bb82b28","repo":"fish2018/pansou","slug":"w-0897af","errorCode":null,"errorMessage":"解析接口响应失败: %w","messagePattern":"解析接口响应失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/panlian/panlian.go","lineNumber":1052,"sourceCode":"\n\t\tbody, readErr := io.ReadAll(resp.Body)\n\t\tresp.Body.Close()\n\t\tcancel()\n\t\tif readErr != nil {\n\t\t\tlastErr = readErr\n\t\t\ttime.Sleep(time.Duration(attempt+1) * 200 * time.Millisecond)\n\t\t\tcontinue\n\t\t}\n\t\tif resp.StatusCode != http.StatusOK {\n\t\t\tlastErr = fmt.Errorf(\"HTTP %d\", resp.StatusCode)\n\t\t\ttime.Sleep(time.Duration(attempt+1) * 200 * time.Millisecond)\n\t\t\tcontinue\n\t\t}\n\t\tif err := json.Unmarshal(body, out); err != nil {\n\t\t\tif bytes.Contains(body, []byte(\"请先登录\")) || bytes.Contains(body, []byte(\"login\")) {\n\t\t\t\treturn fmt.Errorf(\"%w: %s\", errLoginRequired, string(body))\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"解析接口响应失败: %w\", err)\n\t\t}\n\t\treturn nil\n\t}\n\n\treturn lastErr\n}\n\nfunc (p *PanlianPlugin) doLogin(username string, password string, remember bool) (string, *LoginResponse, error) {\n\tusername = strings.TrimSpace(username)\n\tif username == \"\" || password == \"\" {\n\t\treturn \"\", nil, fmt.Errorf(\"账号和密码不能为空\")\n\t}\n\tjar, _ := cookiejar.New(nil)\n\tclient := &http.Client{\n\t\tTimeout: RequestTimeout,\n\t\tJar:     jar,\n\t}\n","sourceCodeStart":1034,"sourceCodeEnd":1070,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/panlian/panlian.go#L1034-L1070","documentation":"Returned when the panlian API response body cannot be unmarshaled into the expected output struct via json.Unmarshal. It means the upstream returned a 200 response whose body is not valid JSON or does not match the target shape, and it is not a login-page response.","triggerScenarios":"Upstream panlian API changed its response format; server returned an HTML error/verification page (without login markers); truncated or corrupted response body; wrong struct passed as out.","commonSituations":"Site deploy changed the JSON schema; CDN/WAF interstitial pages replacing JSON; network proxies injecting content; plugin updated upstream endpoint without updating structs.","solutions":["Log a snippet of the raw body alongside the error to see what the server actually returned","Verify the endpoint URL is still the current panlian API path","Update the target struct fields to match the new upstream JSON schema","Retry the request in case the body was truncated by a transient network issue"],"exampleFix":"// before\nif err := json.Unmarshal(body, out); err != nil {\n    return fmt.Errorf(\"解析接口响应失败: %w\", err)\n}\n// after\nif err := json.Unmarshal(body, out); err != nil {\n    return fmt.Errorf(\"解析接口响应失败: %w; body=%.200s\", err, string(body))\n}","handlingStrategy":"retry","validationCode":"ct := resp.Header.Get(\"Content-Type\")\nif !strings.Contains(ct, \"application/json\") {\n    return fmt.Errorf(\"unexpected content-type %q\", ct)\n}","typeGuard":null,"tryCatchPattern":"if err := fetchJSON(url, &out); err != nil {\n    var uerr *json.UnmarshalTypeError\n    if errors.As(err, &uerr) {\n        log.Printf(\"schema mismatch at %v\", uerr.Struct)\n    }\n    return err\n}","preventionTips":["Check Content-Type is JSON before unmarshaling","Pin/monitor the upstream API shape with a smoke test","Log a body snippet with every unmarshal failure for fast diagnosis"],"tags":["json","unmarshal","http","go"],"backgroundTag":"json-unmarshal-failed","analyzedSha":"beaa56133755a548ebc51b090b3816e2ae044aa6","analyzedAt":"2026-09-07T00:31:18.025Z","contentChangedAt":"2026-09-07T00:31:18.025Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}