fish2018/pansou · error

[ ] unexpected status code: on page

Error message

[%s] unexpected status code: %d on page %d

What it means

searchImpl paginates the Discourse /search.json API. If a page returns a non-2xx status code and no results have been accumulated yet, the plugin aborts and returns this error wrapping the plugin name, status code, and page number. If some results were already collected, it only logs a warning and stops instead.

Solutions

  1. Check the status code printed in the error; if 403/503 the site's Cloudflare protection likely rejected the request — verify scraper/cloudscraper initialization and cookies
  2. Reduce pagination speed or page count to avoid 429 rate limiting
  3. Verify the configured Discourse base URL is correct and reachable in a browser
  4. Retry later if the forum is temporarily down (5xx)
  5. If partial results are acceptable, rely on the warning path (results already present) instead of failing

Example fix

// before
if resp.StatusCode != 200 {
    return nil, fmt.Errorf("[%s] unexpected status code: %d on page %d", p.Name(), resp.StatusCode, currentPage)
}
// after
if resp.StatusCode == 429 {
    time.Sleep(retryBackoff)
    continue // retry the same page
} else if resp.StatusCode != 200 {
    return nil, fmt.Errorf("[%s] unexpected status code: %d on page %d", p.Name(), resp.StatusCode, currentPage)
}
Defensive patterns

Strategy: retry

Validate before calling

// optionally pre-check the site is reachable
resp, err := http.Get(baseURL + "/about.json")
if err != nil || resp.StatusCode != 200 {
    return fmt.Errorf("discourse site unreachable: status=%d err=%v", statusOf(resp), err)
}

Try / catch

links, err := plugin.Search(query)
if err != nil {
    if strings.Contains(err.Error(), "unexpected status code") {
        // parse code, backoff and retry once for 429/5xx
        time.Sleep(backoff)
        links, err = plugin.Search(query)
    }
    if err != nil {
        return fmt.Errorf("search failed: %w", err)
    }
}

Prevention

When it happens

Trigger: Calling Search (via searchImpl) when a Discourse search page request returns e.g. 403, 429, or 500 on the first page fetched (allResults empty).

Common situations: Site blocks requests due to missing/rotated Cloudflare clearance, rate limiting from too-fast pagination, forum being temporarily down, or an incorrect base URL hitting an error page.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07). Data as JSON: /api/errors/bc3222aeed88ad7f. Report an issue: GitHub.

Appendix: source

Thrown at plugin/discourse/discourse.go:232

		resp, err := p.scraper.Get(searchURL)
		if err != nil {
			// 如果已经获取到一些结果,返回已有结果而不是报错
			if len(allResults) > 0 {
				fmt.Printf("[%s] Warning: failed to fetch page %d: %v\n", p.Name(), currentPage, err)
				break
			}
			return nil, fmt.Errorf("[%s] search request failed on page %d: %w", p.Name(), currentPage, err)
		}

		// 检查HTTP状态码
		if resp.StatusCode != 200 {
			resp.Body.Close()
			// 如果已经获取到一些结果,返回已有结果
			if len(allResults) > 0 {
				fmt.Printf("[%s] Warning: unexpected status code %d on page %d\n", p.Name(), resp.StatusCode, currentPage)
				break
			}
			return nil, fmt.Errorf("[%s] unexpected status code: %d on page %d", p.Name(), resp.StatusCode, currentPage)
		}

		// 读取响应体
		body, err := io.ReadAll(resp.Body)
		resp.Body.Close()
		if err != nil {
			if len(allResults) > 0 {
				fmt.Printf("[%s] Warning: failed to read page %d: %v\n", p.Name(), currentPage, err)
				break
			}
			return nil, fmt.Errorf("[%s] read response failed on page %d: %w", p.Name(), currentPage, err)
		}

		// 解析JSON响应
		var searchResp SearchResponse
		if err := json.Unmarshal(body, &searchResp); err != nil {
			if len(allResults) > 0 {
				fmt.Printf("[%s] Warning: failed to parse page %d: %v\n", p.Name(), currentPage, err)

View on GitHub (pinned to beaa561337)