janhq/jan · error · WebSearchError

WebSearchError: {message}

Error message

WebSearchError: {message}

What it means

WebSearchError is the unified error type returned by the web_search and web_fetch Tauri commands in the websearch plugin. It wraps a single message string and is constructed via WebSearchError::new() or the From<String> impl. Because it derives Serialize, it is directly serializable across the Tauri IPC boundary to the frontend.

Source

Thrown at src-tauri/plugins/tauri-plugin-websearch/src/commands.rs:5

use crate::provider::{clamp_count, create_provider, FetchedPage, SearchResult};
use serde::Serialize;

#[derive(Debug, Clone, Serialize, thiserror::Error)]
#[error("WebSearchError: {message}")]
pub struct WebSearchError {
    pub message: String,
}

impl WebSearchError {
    fn new(message: impl Into<String>) -> Self {
        Self {
            message: message.into(),
        }
    }
}

impl From<String> for WebSearchError {
    fn from(message: String) -> Self {
        Self::new(message)
    }
}

View on GitHub (pinned to fad3f12a14)

Solutions

  1. Verify the query/url argument is non-empty and well-formed before invoking the command.
  2. Ensure a valid API key is configured for keyed backends (Exa) in settings.
  3. Check network connectivity and that no proxy/firewall blocks the provider endpoint.
  4. Use the default provider by passing None for provider to fall back to the configured backend.

Example fix

// before
const results = await invoke('web_search', { query: '' });

// after
if (!query.trim()) {
  toast.error('Search query is empty');
  return;
}
const results = await invoke('web_search', { query, provider: 'exa', api_key });
Defensive patterns

Strategy: validation

Validate before calling

// Validate before calling web_search / web_fetch
function validateSearchParams(query: string, provider?: string, apiKey?: string): string | null {
  if (!query.trim()) return "query must not be empty";
  if (provider && !['exa', 'tavily', 'serper'].includes(provider)) return `unknown provider: ${provider}`;
  if (provider === 'exa' && !apiKey) return "Exa requires an API key";
  return null;
}

function validateFetchUrl(url: string): string | null {
  if (!url.trim()) return "url must not be empty";
  if (!/^https?:\/\//.test(url)) return "url must be http(s)";
  return null;
}

Type guard

function isNonEmptyString(v: unknown): v is string {
  return typeof v === 'string' && v.trim().length > 0;
}

function isValidHttpUrl(url: string): boolean {
  try { const u = new URL(url); return u.protocol === 'http:' || u.protocol === 'https:'; }
  catch { return false; }
}

Try / catch

try {
  const results = await invoke('web_search', { query, provider, api_key });
} catch (e) {
  const msg = e?.message ?? String(e);
  if (msg.includes('must not be empty')) {
    toast.error('Please enter a search query');
  } else if (msg.includes('API key')) {
    toast.error('Configure your API key in settings');
  } else {
    toast.error('Search failed', { description: msg });
  }
}

Prevention

When it happens

Trigger: Calling web_search with an empty or whitespace-only query string. Calling web_fetch with a non-http(s) URL or empty URL. create_provider() failing because the provider name is unrecognized or the required API key is missing. The upstream search/fetch backend returning an HTTP error or timing out.

Common situations: Exa API key not set or expired, causing provider authentication failure. Network proxy or firewall blocking the Exa endpoint. User mistyped the provider name (e.g. 'exa' vs 'Exa'). URL with a trailing scheme typo like 'htps://'. Rate-limiting from the search provider.

Related errors


AI-assisted analysis of janhq/jan@fad3f12a14 (2026-08-12). Data as JSON: /api/errors/89a326dacffe43ca. Report an issue: GitHub.