amir20/dozzle · error · Error

cloud search failed

Error message

cloud search failed: ${res.status}

What it means

fetchPage() in cloudLogSearch requests a page of cloud log search results from /api/cloud/search/logs. 204 is treated as an empty page; any other non-OK status throws "cloud search failed: <status>" with the HTTP status code embedded, since the cloud search backend does not return a JSON error body to extract. Callers (runSearch/pagination body) must catch this to keep the UI usable.

Solutions

  1. Read the status code in the thrown message: 401/403 means re-link or fix the cloud API key; 429 means back off and retry later; 5xx means retry or check cloud service status.
  2. Catch the error in runSearch/pagination and surface it as an empty-result + error banner instead of a crash.
  3. Verify the cloud connection is configured (dispatcher linked) before issuing cloud searches.
  4. Retry with exponential backoff for transient 5xx/429; abort in-flight requests when a new query starts (fetchPage already honors AbortSignal).

Example fix

// before
const page = await fetchPage(q, before, signal); // throws on 503
// after
try {
  const page = await fetchPage(q, before, signal);
} catch (e) {
  if ((e as Error).message.includes("503") || (e as Error).message.includes("429")) {
    searchError.value = "Cloud search temporarily unavailable, retrying";
    return null;
  }
  throw e;
}
Defensive patterns

Strategy: retry

Validate before calling

// gate cloud search on a configured cloud connection
if (!config.cloud?.enabled) {
  searchError.value = "Cloud search is not configured";
  return;
}

Try / catch

try {
  const page = await fetchPage(q, before, signal);
} catch (e) {
  if (signal.aborted) return;
  searchError.value = (e as Error).message;
}

Prevention

When it happens

Trigger: Calling runSearch() or requesting the next page while the cloud search endpoint returns 4xx/5xx: no cloud connection configured (502/503), invalid or expired cloud API credentials passed through (401/403), rate limiting on the upstream cloud search (429), or an internal error while querying (500).

Common situations: Dozzle Cloud not linked / no dispatcher configured so the proxy cannot reach the cloud service; API key revoked or on a plan without search; transient cloud-side 5xx while paginating with before cursors; corporate proxy blocking the outbound request.

Related errors


AI-assisted analysis of amir20/dozzle@d9463cbe21 (2026-09-07). Data as JSON: /api/errors/45886511bc4e622a. Report an issue: GitHub.

Appendix: source

Thrown at assets/composable/cloudLogSearch.ts:81

  // pagination request and vice versa.
  let abortController: AbortController | null = null;
  let loadMoreAborter: AbortController | null = null;

  function clearResults() {
    results.value = [];
    error.value = null;
    loading.value = false;
    loadingMore.value = false;
    hasMore.value = false;
    nextBefore.value = 0;
  }

  async function fetchPage(q: string, before: number, signal: AbortSignal): Promise<CloudLogSearchResponse | null> {
    let url = withBase(`/api/cloud/search/logs?q=${encodeURIComponent(q)}&limit=20`);
    if (before > 0) url += `&before=${before}`;
    const res = await fetch(url, { signal });
    if (res.status === 204) return { hits: [], hasMore: false };
    if (!res.ok) throw new Error(`cloud search failed: ${res.status}`);
    return (await res.json()) as CloudLogSearchResponse;
  }

  async function runSearch(q: string) {
    if (abortController) abortController.abort();
    // A fresh query supersedes any in-flight pagination — that page is
    // for the previous query and would be appended onto the wrong result
    // set if it landed late.
    loadMoreAborter?.abort();
    abortController = new AbortController();
    loading.value = true;
    error.value = null;
    nextBefore.value = 0;

    try {
      const body = await fetchPage(q, 0, abortController.signal);
      if (!body) return;
      results.value = body.hits ?? [];

View on GitHub (pinned to d9463cbe21)