fish2018/pansou · error
unexpected status code
Error message
unexpected status code: %d
What it means
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.
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
Example fix
// before
if resp.StatusCode != 200 {
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
}
// after
switch {
case resp.StatusCode == 404:
return nil, fmt.Errorf("topic %d not found", topicID)
case resp.StatusCode == 429:
return nil, fmt.Errorf("rate limited; retry later")
case resp.StatusCode != 200:
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
} Defensive patterns
Strategy: validation
Validate before calling
// validate topic exists before fetching detail
head, _ := http.Head(baseURL + "/t/" + strconv.Itoa(topicID) + ".json")
if head != nil && head.StatusCode == 404 {
return fmt.Errorf("topic %d does not exist", topicID)
} Try / catch
links, err := plugin.GetTopicDetail(id)
if err != nil {
var sc int
if _, scan := fmt.Sscanf(err.Error(), "unexpected status code: %d", &sc); scan == nil {
switch {
case sc == 404:
return nil, ErrTopicNotFound
case sc == 429:
return nil, ErrRateLimited
}
}
return nil, err
} Prevention
- Validate topicIDs before fetching
- Throttle detail requests to avoid 429
- Refresh Cloudflare clearance when seeing 403/503
- Treat 5xx as temporary and retry later
When it happens
Trigger: Topic detail request returns e.g. 403 (Cloudflare block), 404 (topic deleted/ID wrong), 429 (rate limited), or 5xx.
Common situations: 高频抓取触发限流;主题被删除;cloudscraper 会话过期导致 403。
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/328356a5d7271bee.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/discourse/discourse.go:483
func (p *DiscourseAsyncPlugin) GetTopicDetail(topicID int) ([]model.Link, error) {
// 检查 cloudscraper 是否初始化成功
if p.scraper == nil {
return nil, fmt.Errorf("cloudscraper not initialized")
}
// 构建详情URL
detailURL := fmt.Sprintf(detailURLTemplate, topicID)
// 发送详情请求
resp, err := p.scraper.Get(detailURL)
if err != nil {
return nil, fmt.Errorf("detail request failed: %w", err)
}
defer resp.Body.Close()
// 检查HTTP状态码
if resp.StatusCode != 200 {
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
}
// 读取响应体
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("read response failed: %w", err)
}
// 解析JSON响应
var detailResp DetailResponse
if err := json.Unmarshal(body, &detailResp); err != nil {
return nil, fmt.Errorf("parse json failed: %w", err)
}
// 提取第一个帖子的链接
if len(detailResp.PostStream.Posts) == 0 {
return nil, fmt.Errorf("no posts found")
}View on GitHub (pinned to beaa561337)