fish2018/pansou · error

解析搜索地址失败

Error message

解析搜索地址失败: %w

What it means

Wraps an error from url.Parse when constructing the search endpoint URL "%s/_serverFn/%s" from the plugin's baseURL and the server function ID. Indicates the assembled endpoint is not a valid URL — almost always because baseURL is misconfigured rather than a bad functionID (which is a plain hex hash).

Solutions

  1. Print/inspect the exact endpoint string that failed to parse
  2. Validate the configured baseURL: it must include scheme (https://) and contain no spaces or stray '%' escapes
  3. Run url.Parse on the baseURL alone to isolate which part is invalid
  4. Percent-encode or strip invalid characters before composing the endpoint

Example fix

// before
endpoint := fmt.Sprintf("%s/_serverFn/%s", strings.TrimRight(p.baseURL, "/"), functionID)
parsedEndpoint, err := url.Parse(endpoint)
// after: validate the base up front
if _, err := url.Parse(p.baseURL); err != nil {
    return nil, fmt.Errorf("invalid baseURL %q: %w", p.baseURL, err)
}
endpoint := fmt.Sprintf("%s/_serverFn/%s", strings.TrimRight(p.baseURL, "/"), functionID)
parsedEndpoint, err := url.Parse(endpoint)
Defensive patterns

Strategy: validation

Validate before calling

func validateBaseURL(raw string) error {
    u, err := url.Parse(raw)
    if err != nil { return err }
    if u.Scheme == "" || u.Host == "" { return fmt.Errorf("baseURL %q needs scheme and host", raw) }
    return nil
}
if err := validateBaseURL(cfg.XiaokupanBaseURL); err != nil { /* reject config */ }

Type guard

func isUsableURL(u *url.URL) bool { return u != nil && u.Scheme != "" && u.Host != "" }

Try / catch

if err != nil {
    var urlErr *url.Error
    if errors.As(err, &urlErr) {
        log.Printf("malformed endpoint %q: %v", endpoint, urlErr)
    }
    return err
}

Prevention

When it happens

Trigger: url.Parse fails on baseURL+"/_serverFn/"+functionID — e.g. baseURL contains characters illegal in a URL (spaces, unescaped control chars, stray percent signs like 'https://xiaokupan.com%'), or a custom baseURL was set to a malformed value.

Common situations: Operator misconfigures the plugin's base URL (typo, extra protocol, unescaped characters); environment substitution injects whitespace into the URL; copy-pasted URL with trailing invisible characters.

Understand the failure class

Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.

Related errors


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

Appendix: source

Thrown at plugin/xiaokupan/xiaokupan.go:118

		}
		results, err = p.searchWithFunctionID(client, keyword, refreshedID)
	}
	if err != nil {
		return nil, fmt.Errorf("[%s] 搜索失败: %w", p.Name(), err)
	}
	return plugin.FilterResultsByKeyword(results, keyword), nil
}

func (p *XiaokupanPlugin) searchWithFunctionID(client *http.Client, keyword, functionID string) ([]model.SearchResult, error) {
	payload, err := buildSearchPayload(keyword)
	if err != nil {
		return nil, fmt.Errorf("构造搜索参数失败: %w", err)
	}

	endpoint := fmt.Sprintf("%s/_serverFn/%s", strings.TrimRight(p.baseURL, "/"), functionID)
	parsedEndpoint, err := url.Parse(endpoint)
	if err != nil {
		return nil, fmt.Errorf("解析搜索地址失败: %w", err)
	}
	query := parsedEndpoint.Query()
	query.Set("payload", string(payload))
	parsedEndpoint.RawQuery = query.Encode()

	ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
	defer cancel()

	req, err := http.NewRequestWithContext(ctx, http.MethodGet, parsedEndpoint.String(), nil)
	if err != nil {
		return nil, fmt.Errorf("创建搜索请求失败: %w", err)
	}
	p.setSearchHeaders(req, keyword)

	body, err := doLimitedRequest(client, req, maxSearchResponseSize)
	if err != nil {
		return nil, err
	}

View on GitHub (pinned to beaa561337)