fish2018/pansou · error

HTTP 403 Forbidden

Error message

HTTP 403 Forbidden

What it means

fetchDetail in the Gying plugin calls the site's /res/downurl detail endpoint through a cloudscraper session and throws this error when the HTTP response status is exactly 403. The plugin interprets 403 as an expired or invalid session cookie (or an unresolved anti-bot challenge), so the caller is expected to re-login and retry.

Solutions

  1. Re-run the plugin login flow to refresh the session cookie, then retry the search.
  2. Enable DebugLog and inspect the response to see whether it is a challenge page or an auth rejection.
  3. If it persists, check whether the server blocks your IP; route through a different egress or add delay between requests.
  4. Update the plugin/site config (base URL, headers) if the site changed its anti-bot scheme.

Example fix

// before
results, err := plugin.Search(ctx, query) // fails with HTTP 403 Forbidden
// after
if err != nil && strings.Contains(err.Error(), "403") {
    if rerr := plugin.Relogin(ctx); rerr != nil { return rerr }
    results, err = plugin.Search(ctx, query)
}
Defensive patterns

Strategy: retry

Validate before calling

// re-login if session is stale before searching
if time.Since(lastLogin) > sessionTTL {
    if err := plugin.Relogin(ctx); err != nil { return err }
}

Type guard

func isForbidden(err error) bool { return err != nil && strings.Contains(err.Error(), "403") }

Try / catch

results, err := plugin.Search(ctx, kw)
if isForbidden(err) {
    if rerr := plugin.Relogin(ctx); rerr != nil { return rerr }
    results, err = plugin.Search(ctx, kw)
}

Prevention

When it happens

Trigger: Calling fetchDetail (via fetchAllDetails during Search) when the site returns HTTP 403: the stored cookie/session has expired, the Cloudflare-style challenge was not solved, or the server is blocking the client IP/User-Agent.

Common situations: Long-running deployments where the site session rotted overnight; running from a datacenter IP that the site rate-limits or blocks; cloudscraper failing to pass the anti-bot challenge; the site changing its login/challenge flow.

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/c44ed628d4e43c8c. Report an issue: GitHub.

Appendix: source

Thrown at plugin/gying/gying.go:2718

	// 使用cloudscraper发送请求(自动管理Cookie和绕过反爬虫)
	body, statusCode, _, err := p.requestWithChallengeRetry(scraper, http.MethodGet, detailURL, "", "")
	if err != nil {
		if DebugLog {
			fmt.Printf("[Gying]     请求失败: %v\n", err)
		}
		return nil, err
	}

	if DebugLog {
		fmt.Printf("[Gying]     响应状态码: %d\n", statusCode)
	}

	// 检查403错误
	if statusCode == http.StatusForbidden {
		if DebugLog {
			fmt.Printf("[Gying]     ❌ 详情接口返回403 - Cookie可能已过期\n")
		}
		return nil, fmt.Errorf("HTTP 403 Forbidden")
	}

	if statusCode != http.StatusOK {
		if DebugLog {
			fmt.Printf("[Gying]     ❌ HTTP错误: %d\n", statusCode)
		}
		return nil, fmt.Errorf("HTTP %d", statusCode)
	}

	if DebugLog {
		fmt.Printf("[Gying]     响应长度: %d 字节\n", len(body))
	}
	if isLoginShell(body) {
		return nil, fmt.Errorf("HTTP 403 Forbidden")
	}

	var detail DetailData
	if err := json.Unmarshal(body, &detail); err != nil {

View on GitHub (pinned to beaa561337)