{"record":{"id":"c7d7d481a78ee587","repo":"fish2018/pansou","slug":"s-w-c7d7d4","errorCode":null,"errorMessage":"[%s] 解析搜索流失败: %w","messagePattern":"\\[(.+?)\\] 解析搜索流失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/miosou/miosou.go","lineNumber":124,"sourceCode":"\t\tif isAnubisGateResponse(resp) {\n\t\t\tresp.Body.Close()\n\t\t\tcancel()\n\t\t\tp.invalidateGate()\n\t\t\tif err := p.ensureGate(); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tif resp.StatusCode != http.StatusOK {\n\t\t\tresp.Body.Close()\n\t\t\tcancel()\n\t\t\treturn nil, fmt.Errorf(\"[%s] 搜索接口返回状态码: %d\", p.Name(), resp.StatusCode)\n\t\t}\n\t\tgroups, err := parseSearchStream(resp.Body)\n\t\tresp.Body.Close()\n\t\tif err != nil {\n\t\t\tcancel()\n\t\t\treturn nil, fmt.Errorf(\"[%s] 解析搜索流失败: %w\", p.Name(), err)\n\t\t}\n\t\tresults := p.convertGroups(ctx, groups, keyword)\n\t\tcancel()\n\t\treturn results, nil\n\t}\n\treturn nil, fmt.Errorf(\"[%s] 人机验证会话失效\", p.Name())\n}\n\nfunc (p *MiosouPlugin) ensureGate() error {\n\tp.gateMu.Lock()\n\tdefer p.gateMu.Unlock()\n\tif p.gateReady {\n\t\treturn nil\n\t}\n\n\tvar lastErr error\n\tfor attempt := 0; attempt < 3; attempt++ {\n\t\tctx, cancel := context.WithTimeout(context.Background(), gateTimeout)","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/miosou/miosou.go#L106-L142","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Capture the raw stream bytes on failure to see whether it is SSE, HTML, or empty","Update parseSearchStream to match the current SSE event format if the upstream schema changed","Check for proxy/CDN buffering that truncates streaming responses (disable buffering or use direct connection)","Retry — a mid-stream reset is often transient"],"exampleFix":"// before\ngroups, err := parseSearchStream(resp.Body)\nif err != nil {\n    return nil, fmt.Errorf(\"[%s] 解析搜索流失败: %w\", p.Name(), err)\n}\n// after\ndata, _ := io.ReadAll(io.TeeReader(resp.Body, &buf))\ngroups, err := parseSearchStream(bytes.NewReader(data))\nif err != nil {\n    if looksLikeHTML(data) {\n        return nil, fmt.Errorf(\"[%s] 收到 HTML 而非 SSE 流\", p.Name())\n    }\n    return nil, fmt.Errorf(\"[%s] 解析搜索流失败: %w\", p.Name(), err)\n}","handlingStrategy":"try-catch","validationCode":"// Go: sanity-check the stream before parsing\nbr := bufio.NewReader(resp.Body)\nhead, _ := br.Peek(5)\nif !bytes.HasPrefix(head, []byte(\"event\")) && !bytes.HasPrefix(head, []byte(\"data:\")) {\n    // not an SSE stream: abort parse early\n}","typeGuard":"func isSSEStream(head []byte) bool {\n    return bytes.HasPrefix(head, []byte(\"data:\")) || bytes.HasPrefix(head, []byte(\"event:\"))\n}","tryCatchPattern":"groups, err := parseSearchStream(resp.Body)\nif err != nil {\n    log.Printf(\"SSE parse failed: %v\", err)\n    // retry once or surface a clear upstream-format error\n    return nil, fmt.Errorf(\"search stream parse failed: %w\", err)\n}","preventionTips":["Re-validate parseSearchStream against live SSE captures after site updates","Disable proxy/CDN buffering for streaming endpoints","Cap stream size and log tails on parse failure","Distinguish empty streams from malformed frames"],"tags":["sse","stream","parse","upstream"],"backgroundTag":"unexpected-response-shape","analyzedSha":"beaa56133755a548ebc51b090b3816e2ae044aa6","analyzedAt":"2026-09-07T00:31:18.025Z","contentChangedAt":"2026-09-07T00:31:18.025Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}