{"record":{"id":"45886511bc4e622a","repo":"amir20/dozzle","slug":"cloud-search-failed-res-status","errorCode":null,"errorMessage":"cloud search failed: ${res.status}","messagePattern":"cloud search failed: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"assets/composable/cloudLogSearch.ts","lineNumber":81,"sourceCode":"  // pagination request and vice versa.\n  let abortController: AbortController | null = null;\n  let loadMoreAborter: AbortController | null = null;\n\n  function clearResults() {\n    results.value = [];\n    error.value = null;\n    loading.value = false;\n    loadingMore.value = false;\n    hasMore.value = false;\n    nextBefore.value = 0;\n  }\n\n  async function fetchPage(q: string, before: number, signal: AbortSignal): Promise<CloudLogSearchResponse | null> {\n    let url = withBase(`/api/cloud/search/logs?q=${encodeURIComponent(q)}&limit=20`);\n    if (before > 0) url += `&before=${before}`;\n    const res = await fetch(url, { signal });\n    if (res.status === 204) return { hits: [], hasMore: false };\n    if (!res.ok) throw new Error(`cloud search failed: ${res.status}`);\n    return (await res.json()) as CloudLogSearchResponse;\n  }\n\n  async function runSearch(q: string) {\n    if (abortController) abortController.abort();\n    // A fresh query supersedes any in-flight pagination — that page is\n    // for the previous query and would be appended onto the wrong result\n    // set if it landed late.\n    loadMoreAborter?.abort();\n    abortController = new AbortController();\n    loading.value = true;\n    error.value = null;\n    nextBefore.value = 0;\n\n    try {\n      const body = await fetchPage(q, 0, abortController.signal);\n      if (!body) return;\n      results.value = body.hits ?? [];","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/amir20/dozzle/blob/d9463cbe21874e44ab79db6fa63e746ca7d22928/assets/composable/cloudLogSearch.ts#L63-L99","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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)."],"exampleFix":"// before\nconst page = await fetchPage(q, before, signal); // throws on 503\n// after\ntry {\n  const page = await fetchPage(q, before, signal);\n} catch (e) {\n  if ((e as Error).message.includes(\"503\") || (e as Error).message.includes(\"429\")) {\n    searchError.value = \"Cloud search temporarily unavailable, retrying\";\n    return null;\n  }\n  throw e;\n}","handlingStrategy":"retry","validationCode":"// gate cloud search on a configured cloud connection\nif (!config.cloud?.enabled) {\n  searchError.value = \"Cloud search is not configured\";\n  return;\n}","typeGuard":null,"tryCatchPattern":"try {\n  const page = await fetchPage(q, before, signal);\n} catch (e) {\n  if (signal.aborted) return;\n  searchError.value = (e as Error).message;\n}","preventionTips":["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."],"tags":["network","http","cloud","api"],"backgroundTag":"http-non-200-response","analyzedSha":"d9463cbe21874e44ab79db6fa63e746ca7d22928","analyzedAt":"2026-09-07T10:08:55.855Z","contentChangedAt":"2026-09-07T10:08:55.855Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}