{"record":{"id":"b4af4bea86d7434a","repo":"fish2018/pansou","slug":"parse-json-failed-w","errorCode":null,"errorMessage":"parse json failed: %w","messagePattern":"parse json failed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/discourse/discourse.go","lineNumber":495,"sourceCode":"\t\treturn nil, fmt.Errorf(\"detail request failed: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\t// 检查HTTP状态码\n\tif resp.StatusCode != 200 {\n\t\treturn nil, fmt.Errorf(\"unexpected status code: %d\", resp.StatusCode)\n\t}\n\n\t// 读取响应体\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read response failed: %w\", err)\n\t}\n\n\t// 解析JSON响应\n\tvar detailResp DetailResponse\n\tif err := json.Unmarshal(body, &detailResp); err != nil {\n\t\treturn nil, fmt.Errorf(\"parse json failed: %w\", err)\n\t}\n\n\t// 提取第一个帖子的链接\n\tif len(detailResp.PostStream.Posts) == 0 {\n\t\treturn nil, fmt.Errorf(\"no posts found\")\n\t}\n\n\tmainPost := detailResp.PostStream.Posts[0]\n\t\n\t// 从 link_counts 中提取网盘链接\n\tvar links []model.Link\n\tfor _, linkCount := range mainPost.LinkCounts {\n\t\t// 跳过内部链接\n\t\tif linkCount.Internal {\n\t\t\tcontinue\n\t\t}\n\t\t\n\t\t// 判断是否为网盘链接并解析","sourceCodeStart":477,"sourceCodeEnd":513,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/discourse/discourse.go#L477-L513","documentation":"Wrapped decode error in GetTopicDetail (plugin/discourse/discourse.go:495): the topic detail body downloaded successfully but is not valid JSON matching DetailResponse — typically because the site served an HTML challenge/error page with a 200 status. Indicates the response shape is wrong, not the network.","triggerScenarios":"json.Unmarshal fails because the detail endpoint returned non-JSON content, e.g. an HTML Cloudflare challenge, login page, or error page with status 200.","commonSituations":"站点改版导致接口结构变化；返回了被反爬注入脚本的 HTML。","solutions":["Dump the body head to check whether it is HTML instead of JSON","Refresh/repair cloudscraper clearance so real JSON is served","Verify the detail endpoint still returns the expected JSON structure for the forum's Discourse version","Retry in case of a truncated response"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// pre-flight: confirm detail endpoint returns JSON\nresp, _ := http.Get(baseURL + \"/t/1.json\")\nhead, _ := io.ReadAll(io.LimitReader(resp.Body, 1))\nif len(head) == 1 && head[0] != '{' {\n    return errors.New(\"detail endpoint returns non-JSON (bot challenge or login page)\")\n}","typeGuard":null,"tryCatchPattern":"links, err := plugin.GetTopicDetail(id)\nif err != nil && strings.Contains(err.Error(), \"parse json failed\") {\n    log.Printf(\"detail response not JSON; refreshing cloudscraper session\")\n    return nil, ErrBotChallenge\n}","preventionTips":["Keep scraper clearance current","Verify API schema after Discourse upgrades","Check for HTML error pages served with status 200"],"tags":["json","parsing","http","discourse"],"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"}