fish2018/pansou · error

[ ] 解析搜索流失败

Error message

[%s] 解析搜索流失败: %w

What it means

This error is returned by MiosouPlugin.searchImpl when parseSearchStream fails to parse the SSE (text/event-stream) body returned with a 200 status. It wraps the underlying parse error; causes include malformed SSE frames, an empty stream, mid-stream connection reset, or an HTML/error page served with 200.

Solutions

  1. Capture the raw stream bytes on failure to see whether it is SSE, HTML, or empty
  2. Update parseSearchStream to match the current SSE event format if the upstream schema changed
  3. Check for proxy/CDN buffering that truncates streaming responses (disable buffering or use direct connection)
  4. Retry — a mid-stream reset is often transient

Example fix

// before
groups, err := parseSearchStream(resp.Body)
if err != nil {
    return nil, fmt.Errorf("[%s] 解析搜索流失败: %w", p.Name(), err)
}
// after
data, _ := io.ReadAll(io.TeeReader(resp.Body, &buf))
groups, err := parseSearchStream(bytes.NewReader(data))
if err != nil {
    if looksLikeHTML(data) {
        return nil, fmt.Errorf("[%s] 收到 HTML 而非 SSE 流", p.Name())
    }
    return nil, fmt.Errorf("[%s] 解析搜索流失败: %w", p.Name(), err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Go: sanity-check the stream before parsing
br := bufio.NewReader(resp.Body)
head, _ := br.Peek(5)
if !bytes.HasPrefix(head, []byte("event")) && !bytes.HasPrefix(head, []byte("data:")) {
    // not an SSE stream: abort parse early
}

Type guard

func isSSEStream(head []byte) bool {
    return bytes.HasPrefix(head, []byte("data:")) || bytes.HasPrefix(head, []byte("event:"))
}

Try / catch

groups, err := parseSearchStream(resp.Body)
if err != nil {
    log.Printf("SSE parse failed: %v", err)
    // retry once or surface a clear upstream-format error
    return nil, fmt.Errorf("search stream parse failed: %w", err)
}

Prevention

When it happens

Trigger: searchImpl gets StatusCode 200, calls parseSearchStream(resp.Body), and the SSE stream is truncated, empty, contains unexpected event formats, or the connection drops mid-stream so the parser errors out.

Common situations: The API changed its SSE event schema after a site update; an intermediary (proxy/CDN) buffers or cuts long-lived streams; a 200-but-HTML anti-bot page slips past isAnubisGateResponse; network instability aborts the stream halfway.

Related errors


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

Appendix: source

Thrown at plugin/miosou/miosou.go:124

		if isAnubisGateResponse(resp) {
			resp.Body.Close()
			cancel()
			p.invalidateGate()
			if err := p.ensureGate(); err != nil {
				return nil, err
			}
			continue
		}
		if resp.StatusCode != http.StatusOK {
			resp.Body.Close()
			cancel()
			return nil, fmt.Errorf("[%s] 搜索接口返回状态码: %d", p.Name(), resp.StatusCode)
		}
		groups, err := parseSearchStream(resp.Body)
		resp.Body.Close()
		if err != nil {
			cancel()
			return nil, fmt.Errorf("[%s] 解析搜索流失败: %w", p.Name(), err)
		}
		results := p.convertGroups(ctx, groups, keyword)
		cancel()
		return results, nil
	}
	return nil, fmt.Errorf("[%s] 人机验证会话失效", p.Name())
}

func (p *MiosouPlugin) ensureGate() error {
	p.gateMu.Lock()
	defer p.gateMu.Unlock()
	if p.gateReady {
		return nil
	}

	var lastErr error
	for attempt := 0; attempt < 3; attempt++ {
		ctx, cancel := context.WithTimeout(context.Background(), gateTimeout)

View on GitHub (pinned to beaa561337)