fish2018/pansou · error
[ ] 解析会话响应失败
Error message
[%s] 解析会话响应失败: %w
What it means
postSession unmarshals the raw bytes returned by postSessionRaw into nsgameSessionResponse to learn whether the session is active; this error means those bytes are not valid JSON for that struct. Since postSessionRaw already returns a 200 body, this points to a non-JSON body (HTML challenge/interstitial) or a changed session-response schema.
Solutions
- Log the raw body from postSessionRaw on unmarshal failure to see the actual content
- Update nsgameSessionResponse to match the current schema (especially the interface{} Data field's new shape)
- Ensure the challenge/issue steps succeeded and cookies (client.Jar) are retained before querying session status
- Re-run ensureSession from scratch if cookies were lost
- Check for a proxy stripping/rewriting the response
Example fix
// before
if err := json.Unmarshal(data, &response); err != nil {
return false, fmt.Errorf("[%s] 解析会话响应失败: %w", p.Name(), err)
}
// after
if err := json.Unmarshal(data, &response); err != nil {
return false, fmt.Errorf("[%s] 解析会话响应失败(响应开头: %.80q): %w", p.Name(), data, err)
} Defensive patterns
Strategy: validation
Validate before calling
// before trusting session status, ensure the response is JSON
if len(data) > 0 && data[0] != '{' {
return false, fmt.Errorf("session endpoint returned non-JSON body")
} Try / catch
ok, err := plugin.CheckSession()
if err != nil {
log.Warn("session status unknown, re-establishing", "err", err)
if rerr := plugin.RefreshSession(); rerr != nil {
return rerr
}
} Prevention
- Keep the cookie jar intact across requests — losing cookies triggers HTML challenge pages
- Validate bodies start with '{' before unmarshalling session responses
- Update nsgameSessionResponse when the site changes its session API shape
- Re-run the full challenge/issue flow when a status parse fails
When it happens
Trigger: Session status endpoint returns an HTML login/WAF page instead of JSON, an empty body, or JSON whose data field no longer matches nsgameSessionResponse (e.g. data changed from bool to object).
Common situations: Anti-bot cookies were cleared or expired so the site serves a challenge page; site update altered the session response envelope; an intermediary proxy returns its own error page.
Understand the failure class
Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/87316263a28d0f15.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/nsgame/nsgame.go:342
nonce := solveChallenge(challenge.Data.Challenge, challenge.Data.DifficultyBits)
if nonce == "" {
return fmt.Errorf("[%s] 计算访问挑战失败", p.Name())
}
issueBody, _ := json.Marshal(map[string]string{"challenge": challenge.Data.Challenge, "nonce": nonce})
if _, err := p.postSessionRaw(client, "/traffic/session/issue", issueBody); err != nil {
return err
}
return nil
}
func (p *NSGameAsyncPlugin) postSession(client *http.Client, path string, body []byte) (bool, error) {
data, err := p.postSessionRaw(client, path, body)
if err != nil {
return false, err
}
var response nsgameSessionResponse
if err := json.Unmarshal(data, &response); err != nil {
return false, fmt.Errorf("[%s] 解析会话响应失败: %w", p.Name(), err)
}
active, _ := response.Data.(bool)
return active, nil
}
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")View on GitHub (pinned to beaa561337)