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
- 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.
- Catch the error in runSearch/pagination and surface it as an empty-result + error banner instead of a crash.
- Verify the cloud connection is configured (dispatcher linked) before issuing cloud searches.
- 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
- Retry 429/5xx with exponential backoff; fail fast on 401/403 by prompting to re-link the cloud account.
- Always pass an AbortSignal and abort superseded queries (as runSearch does).
- Show the HTTP status from the message in the UI to speed diagnosis.
- Verify the cloud dispatcher/link status before enabling the search box.
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)