fish2018/pansou · error

获取搜索凭证失败

Error message

获取搜索凭证失败: %w

What it means

doSearch fails while obtaining search credentials after refreshing the Action ID: the wrapped error comes from p.getCredentials(keyword, actionID, client), which performs the site's credential API call. This means the plugin could not obtain a valid Sign/Hash/Sha credential even after one Action ID refresh retry. It aborts the search before any results are fetched.

Solutions

  1. Re-run the search to trigger Action ID re-discovery (cached IDs may be stale — clear the in-memory action ID cache or restart the plugin).
  2. Check DebugLog output to see the underlying wrapped error from getCredentials and confirm which HTTP status/parse failure occurred.
  3. Verify network connectivity to BaseURL from the host (curl the site homepage).
  4. Update the plugin to a version matching the current upstream site layout.

Example fix

// before
credentials, err = p.getCredentials(keyword, actionIDs[ActionIDKeys[0]], client)
if err != nil {
    return nil, fmt.Errorf("获取搜索凭证失败: %w", err)
}
// after
credentials, err = p.getCredentials(keyword, actionIDs[ActionIDKeys[0]], client)
if err != nil {
    p.invalidateActionIDCache() // drop possibly stale IDs before returning
    return nil, fmt.Errorf("获取搜索凭证失败: %w", err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

if err := pingBaseURL(BaseURL); err != nil {
    return fmt.Errorf("site unreachable, skipping panyq search: %w", err)
}

Try / catch

results, err := plugin.Search(keyword)
if err != nil {
    if strings.Contains(err.Error(), "获取搜索凭证失败") {
        log.Printf("credential fetch failed (site may have changed): %v", err)
        return fallbackResults(keyword) // use another source
    }
    return err
}

Prevention

When it happens

Trigger: Calling doSearch (or the plugin search entry) when the credential endpoint returns an error — e.g. the site changed its credential API, the Action ID is stale/wrong, the upstream returns non-200, or the response JSON cannot be parsed.

Common situations: Upstream site updated its frontend/obfuscation so action IDs no longer match; network/firewall blocking the site; temporary 5xx from the target site; rate limiting.

Understand the failure class

Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.

Related errors


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

Appendix: source

Thrown at plugin/panyq/panyq.go:223

	actionIDs, err := p.getOrDiscoverActionIDs()
	if err != nil {
		// fmt.Println("panyq: failed to get Action IDs:", err)
		return nil, fmt.Errorf("获取Action ID失败: %w", err)
	}

	// 步骤1: 获取搜索凭证
	credentials, err := p.getCredentials(keyword, actionIDs[ActionIDKeys[0]], client)
	if err != nil {
		// 如果获取凭证失败,尝试刷新Action ID并重试
		actionIDs, err = p.discoverActionIDs()
		if err != nil {
			return nil, fmt.Errorf("刷新Action ID失败: %w", err)
		}
		
		// 使用新的Action ID重试获取凭证
		credentials, err = p.getCredentials(keyword, actionIDs[ActionIDKeys[0]], client)
		if err != nil {
			return nil, fmt.Errorf("获取搜索凭证失败: %w", err)
		}
	}

	// 步骤2: 获取第一页搜索结果列表
	hits, maxPageNum, err := p.getSearchResults(credentials.Sign, 1, client)
	if err != nil {
		return nil, fmt.Errorf("获取搜索结果失败: %w", err)
	}

	if len(hits) == 0 {
		if DebugLog {
			fmt.Println("panyq: no results found for", keyword)
		}
		return []model.SearchResult{}, nil
	}
	
	// 如果有多页结果,并发获取其他页的数据
	if maxPageNum > 1 {

View on GitHub (pinned to beaa561337)