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

  1. If 404, verify the topicID exists on the forum
  2. If 403/503, ensure cloudscraper solved the Cloudflare challenge or refresh the clearance cookie
  3. If 429, slow down request rate and add backoff/retry
  4. 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

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)