{"record":{"id":"bf8763a89ea8a223","repo":"fish2018/pansou","slug":"w-bf8763","errorCode":null,"errorMessage":"解析登录响应失败: %w","messagePattern":"解析登录响应失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/panlian/panlian.go","lineNumber":1127,"sourceCode":"\tresp, err = client.Do(req)\n\tif err != nil {\n\t\tcancel()\n\t\treturn \"\", nil, err\n\t}\n\tdefer resp.Body.Close()\n\n\trespBody, err := io.ReadAll(resp.Body)\n\tcancel()\n\tif err != nil {\n\t\treturn \"\", nil, err\n\t}\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn \"\", nil, fmt.Errorf(\"登录请求失败: HTTP %d\", resp.StatusCode)\n\t}\n\n\tvar loginResp LoginResponse\n\tif err := json.Unmarshal(respBody, &loginResp); err != nil {\n\t\treturn \"\", nil, fmt.Errorf(\"解析登录响应失败: %w\", err)\n\t}\n\tif !loginResp.Success {\n\t\treturn \"\", nil, errors.New(strings.TrimSpace(loginResp.Message))\n\t}\n\n\tcookieString := cookiesToString(jar.Cookies(baseURL))\n\tif cookieString == \"\" {\n\t\treturn \"\", nil, fmt.Errorf(\"登录成功但未获取到有效 Cookie\")\n\t}\n\n\treturn cookieString, &loginResp, nil\n}\n\nfunc (p *PanlianPlugin) reloginUser(user *User) error {\n\tpassword, err := p.decryptPassword(user.EncryptedPassword)\n\tif err != nil {\n\t\treturn err\n\t}","sourceCodeStart":1109,"sourceCodeEnd":1145,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/panlian/panlian.go#L1109-L1145","documentation":"The login POST returned HTTP 200 but the body could not be parsed as a LoginResponse JSON object. This usually means the server replied with HTML (error page, captcha page, or redirect) instead of the expected JSON.","triggerScenarios":"json.Unmarshal(respBody, &loginResp) fails because the login endpoint returned HTML or malformed/non-JSON content despite a 200 status.","commonSituations":"Site added captcha or changed the login endpoint to return HTML; WAF interstitial served with status 200; endpoint moved and an HTML 404 page was served with 200 via SPA fallback.","solutions":["Log the first bytes of respBody to identify what was actually returned","Verify the login endpoint URL against the current site (open dev tools, re-capture the request)","Update the LoginResponse struct if the JSON schema changed","Handle captcha/verification requirements if present in the HTML"],"exampleFix":"// before\nvar loginResp LoginResponse\nif err := json.Unmarshal(respBody, &loginResp); err != nil {\n    return \"\", nil, fmt.Errorf(\"解析登录响应失败: %w\", err)\n}\n// after\nvar loginResp LoginResponse\nif err := json.Unmarshal(respBody, &loginResp); err != nil {\n    return \"\", nil, fmt.Errorf(\"解析登录响应失败: %w; body=%.200s\", err, string(respBody))\n}","handlingStrategy":"validation","validationCode":"ct := resp.Header.Get(\"Content-Type\")\nif !strings.Contains(ct, \"application/json\") {\n    return fmt.Errorf(\"login returned non-JSON: %s\", ct)\n}","typeGuard":null,"tryCatchPattern":"if err := json.Unmarshal(respBody, &loginResp); err != nil {\n    log.Printf(\"login body: %.200s\", string(respBody))\n    return fmt.Errorf(\"解析登录响应失败: %w\", err)\n}","preventionTips":["Check Content-Type before unmarshaling the login response","Keep the login endpoint URL in sync with the live site","Log response body snippets on parse failure"],"tags":["json","unmarshal","login","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"}