Tencent/WeKnora · error

invalid SearXNG base_url: %w

Error message

invalid SearXNG base_url: %w

What it means

ValidateSearxngBaseURL rejects a configured SearXNG base URL when utils.ValidateURLForSSRF reports it unsafe (private/loopback/link-local hosts, disallowed schemes, etc.). The check exists to prevent server-side request forgery, since the provider will issue HTTP GETs to whatever base URL is configured. The underlying SSRF reason is wrapped via %w so errors.Is/As still works.

Source

Thrown at internal/infrastructure/web_search/searxng.go:56

// the service-layer parameter validation and the provider constructor so that
// "save" and "use" never disagree.
func ValidateSearxngBaseURL(rawURL string) error {
	base := strings.TrimSpace(rawURL)
	if base == "" {
		return fmt.Errorf("base_url is required for SearXNG provider")
	}
	parsed, err := url.Parse(base)
	if err != nil || parsed.Scheme == "" || parsed.Host == "" {
		return fmt.Errorf("invalid SearXNG base_url: must be an absolute http(s) URL")
	}
	if parsed.Scheme != "http" && parsed.Scheme != "https" {
		return fmt.Errorf("invalid SearXNG base_url scheme: %s", parsed.Scheme)
	}
	if parsed.RawQuery != "" || parsed.Fragment != "" {
		return fmt.Errorf("invalid SearXNG base_url: must not contain query or fragment")
	}
	if err := utils.ValidateURLForSSRF(base); err != nil {
		return fmt.Errorf("invalid SearXNG base_url: %w", err)
	}
	return nil
}

// NewSearxngProvider builds a SearXNG provider from tenant parameters.
func NewSearxngProvider(params types.WebSearchProviderParameters) (interfaces.WebSearchProvider, error) {
	base := strings.TrimSpace(params.BaseURL)
	if err := ValidateSearxngBaseURL(base); err != nil {
		return nil, err
	}

	client, err := NewSearchHTTPClient(defaultSearxngTimeout, params.ProxyURL)
	if err != nil {
		return nil, err
	}
	return &SearxngProvider{
		client:  client,
		baseURL: strings.TrimRight(base, "/"),

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Host SearXNG on an address the SSRF validator allows (public DNS name or an explicitly allow-listed host)
  2. Check the wrapped error with errors.Unwrap/errors.Is to see which SSRF rule fired (private range, loopback, scheme)
  3. If self-hosting is intentional, use the library's documented allowlist mechanism to permit the internal host, or deploy SearXNG behind an allowed gateway
  4. Verify the URL has no query/fragment and uses http/https, which are rejected earlier in the same function

Example fix

// before
provider, err := NewSearxngProvider(types.WebSearchProviderParameters{BaseURL: "http://127.0.0.1:8888"})
// after
provider, err := NewSearxngProvider(types.WebSearchProviderParameters{BaseURL: "https://searxng.example.com"})
Defensive patterns

Strategy: validation

Validate before calling

func validSearxngBase(u string) bool {
    p, err := url.Parse(strings.TrimSpace(u))
    if err != nil || (p.Scheme != "http" && p.Scheme != "https") || p.RawQuery != "" || p.Fragment != "" {
        return false
    }
    host := p.Hostname()
    ip := net.ParseIP(host)
    if ip != nil && (ip.IsLoopback() || ip.IsPrivate() || ip.IsLinkLocalUnicast()) {
        return false
    }
    return host != "localhost" && host != ""
}

Prevention

When it happens

Trigger: Calling NewSearxngProvider with params whose BaseURL points at a private IP (e.g. 127.0.0.1, 10.x, 192.168.x), localhost, metadata endpoints (169.254.169.254), or a non-http(s) scheme that passes earlier checks but fails SSRF validation.

Common situations: Self-hosted SearXNG running on localhost or the Docker network (192.168.x) inside a cluster where SSRF policy blocks internal addresses; misconfigured tenant web-search settings pointing at an internal search appliance.

Related errors


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/83bad85164f6d61f. Report an issue: GitHub.