fish2018/pansou · error

解析接口响应失败

Error message

解析接口响应失败: %w

What it means

Returned when the panlian API response body cannot be unmarshaled into the expected output struct via json.Unmarshal. It means the upstream returned a 200 response whose body is not valid JSON or does not match the target shape, and it is not a login-page response.

Solutions

  1. Log a snippet of the raw body alongside the error to see what the server actually returned
  2. Verify the endpoint URL is still the current panlian API path
  3. Update the target struct fields to match the new upstream JSON schema
  4. Retry the request in case the body was truncated by a transient network issue

Example fix

// before
if err := json.Unmarshal(body, out); err != nil {
    return fmt.Errorf("解析接口响应失败: %w", err)
}
// after
if err := json.Unmarshal(body, out); err != nil {
    return fmt.Errorf("解析接口响应失败: %w; body=%.200s", err, string(body))
}
Defensive patterns

Strategy: retry

Validate before calling

ct := resp.Header.Get("Content-Type")
if !strings.Contains(ct, "application/json") {
    return fmt.Errorf("unexpected content-type %q", ct)
}

Try / catch

if err := fetchJSON(url, &out); err != nil {
    var uerr *json.UnmarshalTypeError
    if errors.As(err, &uerr) {
        log.Printf("schema mismatch at %v", uerr.Struct)
    }
    return err
}

Prevention

When it happens

Trigger: Upstream panlian API changed its response format; server returned an HTML error/verification page (without login markers); truncated or corrupted response body; wrong struct passed as out.

Common situations: Site deploy changed the JSON schema; CDN/WAF interstitial pages replacing JSON; network proxies injecting content; plugin updated upstream endpoint without updating structs.

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

Appendix: source

Thrown at plugin/panlian/panlian.go:1052

		body, readErr := io.ReadAll(resp.Body)
		resp.Body.Close()
		cancel()
		if readErr != nil {
			lastErr = readErr
			time.Sleep(time.Duration(attempt+1) * 200 * time.Millisecond)
			continue
		}
		if resp.StatusCode != http.StatusOK {
			lastErr = fmt.Errorf("HTTP %d", resp.StatusCode)
			time.Sleep(time.Duration(attempt+1) * 200 * time.Millisecond)
			continue
		}
		if err := json.Unmarshal(body, out); err != nil {
			if bytes.Contains(body, []byte("请先登录")) || bytes.Contains(body, []byte("login")) {
				return fmt.Errorf("%w: %s", errLoginRequired, string(body))
			}
			return fmt.Errorf("解析接口响应失败: %w", err)
		}
		return nil
	}

	return lastErr
}

func (p *PanlianPlugin) doLogin(username string, password string, remember bool) (string, *LoginResponse, error) {
	username = strings.TrimSpace(username)
	if username == "" || password == "" {
		return "", nil, fmt.Errorf("账号和密码不能为空")
	}
	jar, _ := cookiejar.New(nil)
	client := &http.Client{
		Timeout: RequestTimeout,
		Jar:     jar,
	}

View on GitHub (pinned to beaa561337)