fish2018/pansou · error

[ ] 会话请求失败

Error message

[%s] 会话请求失败: %w

What it means

nsgame's postSessionRaw wraps any error returned by the HTTP client's Do() when POSTing to the session endpoints (/traffic/session/challenge or /traffic/session/issue). It prefixes the plugin name and the Chinese text '会话请求失败' (session request failed) and preserves the underlying error via %w. This means the request never got an HTTP response at all — it failed at the transport level (DNS, TCP, TLS, timeout, or context cancellation).

Solutions

  1. Check basic connectivity to the site: curl -v https://nsthwj.cn/ from the machine running the plugin.
  2. Unwrap the error with errors.Unwrap or inspect with errors.As(*url.Error)/net.Error to see the root cause (timeout vs DNS vs TLS).
  3. Configure a proxy (HTTP_PROXY/HTTPS_PROXY or the plugin's client transport) if the site is unreachable from your network.
  4. Increase the http.Client Timeout if errors.As reveals context deadline exceeded on slow endpoints.
  5. Retry with backoff; transient connection resets are common with such sites.

Example fix

// before
resp, err := client.Do(req)
if err != nil {
    return nil, fmt.Errorf("[%s] 会话请求失败: %w", p.Name(), err)
}
// after
resp, err := client.Do(req)
if err != nil {
    var nerr net.Error
    if errors.As(err, &nerr) && nerr.Timeout() {
        return nil, fmt.Errorf("[%s] 会话请求超时: %w", p.Name(), err)
    }
    return nil, fmt.Errorf("[%s] 会话请求失败: %w", p.Name(), err)
}
Defensive patterns

Strategy: retry

Validate before calling

// preflight
if _, err := net.LookupHost("nsthwj.cn"); err != nil { return fmt.Errorf("DNS 解析失败: %w", err) }

Type guard

var nerr net.Error
isNetworkTimeout := errors.As(err, &nerr) && nerr.Timeout()

Try / catch

data, err := p.postSessionRaw(client, path, body)
if err != nil {
    var nerr net.Error
    if errors.As(err, &nerr) && nerr.Timeout() {
        // retry with backoff
    } else {
        return fmt.Errorf("session transport failed: %w", err)
    }
}

Prevention

When it happens

Trigger: p.postSessionRaw calls client.Do(req) and err != nil: DNS resolution failure for nsthwj.cn, TCP connect failure, TLS handshake failure, context/deadline exceeded, or connection reset before any response bytes arrive. Raised from ensureSession (challenge/issue calls) and postSession.

Common situations: Target site nsthwj.cn is down or blocked (GFW/regional block), no network in a container, missing proxy config, TLS cert issues with a MITM proxy, or the site drops long-lived connections so a stale client fails.

Related errors


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

Appendix: source

Thrown at plugin/nsgame/nsgame.go:365

func (p *NSGameAsyncPlugin) postSessionRaw(client *http.Client, path string, body []byte) ([]byte, error) {
	ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
	defer cancel()
	var reader io.Reader
	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

View on GitHub (pinned to beaa561337)