fish2018/pansou · error

[ ] 会话返回状态码

Error message

[%s] 会话返回状态码: %d

What it means

postSessionRaw treats any non-200 HTTP status from the session endpoints as a hard failure and returns this error containing the numeric status code. Unlike the transport error, an actual HTTP response was received but the server rejected the session establishment (challenge fetch or issue call).

Solutions

  1. Log the response body (currently read into data but not included in the error) to see the server's actual rejection reason.
  2. Check for rate limiting (429) and add delays/backoff between session attempts.
  3. If 403, inspect whether required cookies/headers (set by p.setRequestHeaders) match what the site currently expects.
  4. Retry after a delay for 5xx; the site may be under maintenance.
  5. Verify the site API hasn't changed (check in browser devtools) if you consistently get 404/410.

Example fix

// before
if resp.StatusCode != http.StatusOK {
    return nil, fmt.Errorf("[%s] 会话返回状态码: %d", p.Name(), resp.StatusCode)
}
// after
if resp.StatusCode != http.StatusOK {
    return nil, fmt.Errorf("[%s] 会话返回状态码: %d, body: %s", p.Name(), resp.StatusCode, string(data))
}
Defensive patterns

Strategy: try-catch

Validate before calling

null

Type guard

null

Try / catch

data, err := p.postSessionRaw(client, path, body)
if err != nil {
    var httpErr *HTTPStatusError // if you extend the plugin to use one
    if errors.As(err, &httpErr) && httpErr.Code == http.StatusTooManyRequests {
        time.Sleep(backoff)
        // retry
    }
}

Prevention

When it happens

Trigger: After client.Do succeeds, resp.StatusCode != http.StatusOK when POSTing to /traffic/session/challenge or /traffic/session/issue. Note the error is returned before reading the body, so the response body (which may contain the actual server error reason) is discarded.

Common situations: Site returns 403/429 due to anti-bot protection (the plugin does solve a PoW challenge, but the site may still block), 5xx during server maintenance, or a Cloudflare challenge page replacing the API response.

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

Appendix: source

Thrown at plugin/nsgame/nsgame.go:370

	if body != nil {
		reader = strings.NewReader(string(body))
	}
	req, err := http.NewRequestWithContext(ctx, http.MethodPost, baseURL+path, reader)
	if err != nil {
		return nil, fmt.Errorf("[%s] 创建会话请求失败: %w", p.Name(), err)
	}
	if body != nil {
		req.Header.Set("Content-Type", "application/json")
	}
	p.setRequestHeaders(req, "https://nsthwj.cn/")
	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("[%s] 会话请求失败: %w", p.Name(), err)
	}
	defer resp.Body.Close()
	data, _ := io.ReadAll(resp.Body)
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("[%s] 会话返回状态码: %d", p.Name(), resp.StatusCode)
	}
	return data, nil
}

func solveChallenge(challenge string, difficultyBits int) string {
	for nonce := int64(0); nonce < 10000000; nonce++ {
		sum := sha256.Sum256([]byte(fmt.Sprintf("%s:%d", challenge, nonce)))
		fullBytes, remainder := difficultyBits/8, difficultyBits%8
		valid := true
		for i := 0; i < fullBytes; i++ {
			if sum[i] != 0 {
				valid = false
				break
			}
		}
		if valid && remainder > 0 && sum[fullBytes]>>(8-remainder) != 0 {
			valid = false
		}

View on GitHub (pinned to beaa561337)