fish2018/pansou · error

[ ] API 搜索请求失败

Error message

[%s] API 搜索请求失败: %w

What it means

The Flarum API request in dyyj.executeSearchAPI failed at the transport level after doRequestWithRetry exhausted retries. Wraps DNS/TCP/TLS errors and context deadline exceeded. Retries have already occurred; this is the terminal failure.

Solutions

  1. curl -v the same apiURL from the host to classify the failure
  2. Check DNS resolution of the BaseURL host
  3. Configure proxy environment variables if egress requires one
  4. Increase RequestTimeout for slow API responses
  5. Check whether the site moved/renamed its domain

Example fix

// before
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
	return nil, fmt.Errorf("[%s] API 搜索请求失败: %w", p.Name(), err)
}
// after
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
	if errors.Is(err, context.DeadlineExceeded) {
		return nil, fmt.Errorf("[%s] API 搜索超时(>%s): %w", p.Name(), RequestTimeout, err)
	}
	return nil, fmt.Errorf("[%s] API 搜索请求失败: %w", p.Name(), err)
}
Defensive patterns

Strategy: retry

Validate before calling

conn, err := net.DialTimeout("tcp", host+":443", 3*time.Second)
if err != nil { return errors.New("API host unreachable") }; conn.Close()

Try / catch

resp, err := p.doRequestWithRetry(req, client)
if err != nil {
	var netErr net.Error
	if errors.As(err, &netErr) && netErr.Timeout() {
		return nil, fmt.Errorf("API timeout: %w", err)
	}
	return nil, fmt.Errorf("API transport error: %w", err)
}

Prevention

When it happens

Trigger: p.doRequestWithRetry(req, client) errored on GET {BaseURL}/api/discussions?... — DNS failure, connection refused/reset, TLS error, or RequestTimeout exceeded.

Common situations: API host unreachable from the deployment network; site blocked datacenter IPs; API endpoint removed (connection-level symptom less common — usually 404, but domain loss gives DNS errors); proxy required but unset.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at plugin/dyyj/dyyj.go:423

	params.Set("filter[q]", keyword)
	params.Set("include", "mostRelevantPost")
	params.Set("page[limit]", fmt.Sprintf("%d", MaxResults))
	apiURL := BaseURL + "/api/discussions?" + params.Encode()

	ctx, cancel := context.WithTimeout(context.Background(), RequestTimeout)
	defer cancel()
	req, err := http.NewRequestWithContext(ctx, http.MethodGet, apiURL, nil)
	if err != nil {
		return nil, fmt.Errorf("[%s] 创建 API 请求失败: %w", p.Name(), err)
	}
	req.Header.Set("User-Agent", UserAgent)
	req.Header.Set("Accept", "application/vnd.api+json, application/json")
	req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
	req.Header.Set("Referer", BaseURL+"/")

	resp, err := p.doRequestWithRetry(req, client)
	if err != nil {
		return nil, fmt.Errorf("[%s] API 搜索请求失败: %w", p.Name(), err)
	}
	defer resp.Body.Close()

	var payload dyyjAPIResponse
	if err := encodingjson.NewDecoder(resp.Body).Decode(&payload); err != nil {
		return nil, fmt.Errorf("[%s] 解析 API 响应失败: %w", p.Name(), err)
	}

	posts := make(map[string]dyyjIncluded, len(payload.Included))
	for _, post := range payload.Included {
		posts[post.ID] = post
	}

	results := make([]model.SearchResult, 0, len(payload.Data))
	for _, discussion := range payload.Data {
		post, ok := posts[discussion.Relationships.MostRelevantPost.Data.ID]
		if !ok {
			continue

View on GitHub (pinned to beaa561337)