fish2018/pansou · error

[ ] 重试 次后仍然失败

Error message

[%s] 重试 %d 次后仍然失败: %w

What it means

doRequestWithRetry returns this after its final retry attempt also failed; lastErr is the error from the last attempt. Both the token fetch and the search request go through this helper, so any persistent network problem against the upstream site surfaces as this wrapped error. It signals the transport is unusable, not an HTTP-level status problem.

Solutions

  1. Unwrap lastErr to identify the root cause (timeout vs refused vs TLS) and fix accordingly
  2. Test reachability with curl from the deployment host; configure proxy env vars if needed
  3. Increase maxRetries or add exponential backoff for transient failures
  4. Increase the 30s request timeout if the upstream is consistently slow
  5. Retry the whole operation later; if persistent, check upstream site status

Example fix

// before
return nil, fmt.Errorf("[%s] 重试 %d 次后仍然失败: %w", p.Name(), maxRetries, lastErr)
// after
return nil, fmt.Errorf("[%s] 重试 %d 次后仍然失败 (最后一次: %v): %w", p.Name(), maxRetries, lastErr, lastErr)
Defensive patterns

Strategy: retry

Validate before calling

// health probe before search
resp, err := client.Head(BaseURL)
if err != nil { return fmt.Errorf("upstream unreachable: %w", err) }
resp.Body.Close()

Try / catch

if err != nil {
    var ne net.Error
    if errors.As(err, &ne) && ne.Timeout() {
        return retryAfter(2 * time.Second)
    }
    return nil, err // non-retryable transport failure
}

Prevention

When it happens

Trigger: All maxRetries attempts in the loop returned err (dial timeout, connection refused/reset, TLS errors, context deadline exceeded) and the loop exits, wrapping lastErr.

Common situations: Site unreachable from the user's network/region; firewall or GFW blocking; DNS failure; the 30s per-request timeout expiring on a slow upstream; server-side rate limiting resetting connections.

Related errors


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

Appendix: source

Thrown at plugin/xys/xys.go:233

			backoff := time.Duration(1<<uint(i-1)) * 200 * time.Millisecond
			time.Sleep(backoff)
		}
		
		// 克隆请求避免并发问题
		reqClone := req.Clone(req.Context())
		
		resp, err := client.Do(reqClone)
		if err == nil && resp.StatusCode == 200 {
			return resp, nil
		}
		
		if resp != nil {
			resp.Body.Close()
		}
		lastErr = err
	}
	
	return nil, fmt.Errorf("[%s] 重试 %d 次后仍然失败: %w", p.Name(), maxRetries, lastErr)
}

// executeSearch 执行搜索请求
func (p *XysPlugin) executeSearch(client *http.Client, token, keyword string) ([]model.SearchResult, error) {
	// 构建搜索URL
	searchURL := fmt.Sprintf("%s%s?DToken2=%s&requestID=undefined&mode=90002&stype=undefined&scope_content=0&wd=%s&uk=&page=1&limit=20&screen_filetype=",
		BaseURL, SearchPath, token, url.QueryEscape(keyword))

	// 创建带超时的上下文
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	req, err := http.NewRequestWithContext(ctx, "POST", searchURL, nil)
	if err != nil {
		return nil, fmt.Errorf("[%s] 创建搜索请求失败: %w", p.Name(), err)
	}

	// 设置完整的请求头

View on GitHub (pinned to beaa561337)