{"record":{"id":"328356a5d7271bee","repo":"fish2018/pansou","slug":"unexpected-status-code-d","errorCode":null,"errorMessage":"unexpected status code: %d","messagePattern":"unexpected status code: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/discourse/discourse.go","lineNumber":483,"sourceCode":"func (p *DiscourseAsyncPlugin) GetTopicDetail(topicID int) ([]model.Link, error) {\n\t// 检查 cloudscraper 是否初始化成功\n\tif p.scraper == nil {\n\t\treturn nil, fmt.Errorf(\"cloudscraper not initialized\")\n\t}\n\n\t// 构建详情URL\n\tdetailURL := fmt.Sprintf(detailURLTemplate, topicID)\n\n\t// 发送详情请求\n\tresp, err := p.scraper.Get(detailURL)\n\tif err != nil {\n\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}","sourceCodeStart":465,"sourceCodeEnd":501,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/discourse/discourse.go#L465-L501","documentation":"Status-code error in GetTopicDetail (plugin/discourse/discourse.go:483): the topic detail endpoint answered with a status other than 200. The Discourse site was reachable but rejected or redirected the request (rate limit, Cloudflare challenge, deleted topic). Callers should treat it as a site-side rejection, not a parsing problem.","triggerScenarios":"Topic detail request returns e.g. 403 (Cloudflare block), 404 (topic deleted/ID wrong), 429 (rate limited), or 5xx.","commonSituations":"高频抓取触发限流；主题被删除；cloudscraper 会话过期导致 403。","solutions":["If 404, verify the topicID exists on the forum","If 403/503, ensure cloudscraper solved the Cloudflare challenge or refresh the clearance cookie","If 429, slow down request rate and add backoff/retry","If 5xx, retry later; the forum may be temporarily down"],"exampleFix":"// before\nif resp.StatusCode != 200 {\n    return nil, fmt.Errorf(\"unexpected status code: %d\", resp.StatusCode)\n}\n// after\nswitch {\ncase resp.StatusCode == 404:\n    return nil, fmt.Errorf(\"topic %d not found\", topicID)\ncase resp.StatusCode == 429:\n    return nil, fmt.Errorf(\"rate limited; retry later\")\ncase resp.StatusCode != 200:\n    return nil, fmt.Errorf(\"unexpected status code: %d\", resp.StatusCode)\n}","handlingStrategy":"validation","validationCode":"// validate topic exists before fetching detail\nhead, _ := http.Head(baseURL + \"/t/\" + strconv.Itoa(topicID) + \".json\")\nif head != nil && head.StatusCode == 404 {\n    return fmt.Errorf(\"topic %d does not exist\", topicID)\n}","typeGuard":null,"tryCatchPattern":"links, err := plugin.GetTopicDetail(id)\nif err != nil {\n    var sc int\n    if _, scan := fmt.Sscanf(err.Error(), \"unexpected status code: %d\", &sc); scan == nil {\n        switch {\n        case sc == 404:\n            return nil, ErrTopicNotFound\n        case sc == 429:\n            return nil, ErrRateLimited\n        }\n    }\n    return nil, err\n}","preventionTips":["Validate topicIDs before fetching","Throttle detail requests to avoid 429","Refresh Cloudflare clearance when seeing 403/503","Treat 5xx as temporary and retry later"],"tags":["http","status-code","cloudflare","discourse"],"backgroundTag":"http-non-200-response","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"}