fish2018/pansou · warning

detail status=

Error message

detail status=%d

What it means

fetchDiscussionLinks fetches a single discussion detail page via p.scraper.Get and returns this compact error when the HTTP status is not 200. The caller (detail worker goroutine) logs a warning and skips that discussion's links rather than failing the whole search.

Solutions

  1. Check the status code in the error — 404 means the discussion is gone and should be skipped
  2. Slow down detail requests or lower detailWorkers to avoid 429
  3. Pass Cloudflare challenges via cloudscraper updates or proxies for 403/503
  4. Treat it as non-fatal: the search already degrades gracefully by dropping that result
Defensive patterns

Strategy: fallback

Try / catch

if err != nil {
    if strings.Contains(err.Error(), "detail status=404") {
        // discussion deleted: drop result silently
    } else if strings.Contains(err.Error(), "detail status=429") {
        // rate limited: back off detail workers
    }
}

Prevention

When it happens

Trigger: GET of /api/discussions/{id} returns 403 (Cloudflare block), 404 (discussion deleted), or 429/5xx while resolving download links for one search result.

Common situations: Individual discussion removed since indexing; per-IP rate limiting on detail requests; Cloudflare challenge triggered by bursts of detail fetches (4 concurrent workers).

Related errors


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

Appendix: source

Thrown at plugin/panzun/panzun.go:281

	for _, item := range ordered {
		if item.ok {
			results = append(results, item.result)
		}
	}

	return results, nil
}

func (p *PanzunPlugin) fetchDiscussionLinks(client *http.Client, discussionID string) ([]model.Link, string, []string, time.Time, error) {
	detailURL := fmt.Sprintf("%s/discussions/%s", apiBase, discussionID)
	resp, err := p.scraper.Get(detailURL)
	if err != nil {
		return nil, "", nil, time.Time{}, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, "", nil, time.Time{}, fmt.Errorf("detail status=%d", resp.StatusCode)
	}

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, "", nil, time.Time{}, err
	}

	var detailResp struct {
		Data     Discussion         `json:"data"`
		Included []IncludedResource `json:"included"`
	}
	if err := jsonutil.Unmarshal(body, &detailResp); err != nil {
		return nil, "", nil, time.Time{}, err
	}

	includedMap := buildIncludedMap(detailResp.Included)
	rel := detailResp.Data.Relationships

View on GitHub (pinned to beaa561337)