{"record":{"id":"1b18e4ec67f31265","repo":"amir20/dozzle","slug":"no-reader-available-from-stream","errorCode":null,"errorMessage":"No reader available from stream","messagePattern":"No reader available from stream","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"assets/components/LogViewer/LogAnalytics.vue","lineNumber":148,"sourceCode":"  `/api/hosts/${container.host}/containers/${container.id}/logs?stdout=1&stderr=1&everything&jsonOnly`,\n);\n\nconst [{ useDuckDB }, response] = await Promise.all([import(`@/composable/duckdb`), fetch(url)]);\n\nif (!response.ok) {\n  console.log(\"error fetching logs from\", url);\n  throw new Error(`Failed to fetch logs: ${response.statusText}`);\n}\n\nconst { db, conn } = await useDuckDB();\nconst empty = await conn.query<Record<string, any>>(`SELECT 1 LIMIT 0`);\n\nonMounted(async () => {\n  try {\n    state.value = \"downloading\";\n\n    const reader = response.body?.getReader();\n    if (!reader) throw new Error(\"No reader available from stream\");\n\n    const chunks: Uint8Array[] = [];\n    bytes.value = 0;\n\n    while (true) {\n      const { done, value } = await reader.read();\n      if (done) break;\n      chunks.push(value);\n      bytes.value += value.length;\n    }\n\n    const arrayBuffer = new Uint8Array(bytes.value);\n    let position = 0;\n    for (const chunk of chunks) {\n      arrayBuffer.set(chunk, position);\n      position += chunk.length;\n    }\n","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/amir20/dozzle/blob/d9463cbe21874e44ab79db6fa63e746ca7d22928/assets/components/LogViewer/LogAnalytics.vue#L130-L166","documentation":"After the fetch succeeds at the HTTP level, LogAnalytics.vue calls response.body?.getReader() to stream the payload. response.body is null when the response has no body or when the environment does not support streaming response bodies (e.g. older browsers, certain service-worker/proxy interceptors). The code throws this error instead of dereferencing null.","triggerScenarios":"response.body is undefined/null: browser lacks ReadableStream body support (older Safari, some WebView), a service worker stripped or buffered the body, the response is an opaque/cors-filtered response, or the response completed with empty body.","commonSituations":"Users on outdated browsers or embedded WebViews opening the analytics page; an extension or service worker intercepting fetch and returning a synthetic Response without a body; corporate proxies altering the response.","solutions":["Update to a browser that supports ReadableStream response bodies (all modern Chrome/Firefox/Safari 10.1+)","Disable fetch-intercepting extensions or service workers and retry","Ensure the analytics request is same-origin/CORS-clean so the body is not opaque","Add a fallback to consume the response as a blob/arrayBuffer when getReader is unavailable"],"exampleFix":"// before\nconst reader = response.body?.getReader();\nif (!reader) throw new Error(\"No reader available from stream\");\n// after\nif (!response.body) {\n  const blob = await response.blob();\n  chunks.push(new Uint8Array(await blob.arrayBuffer()));\n} else {\n  const reader = response.body.getReader();\n  // ...stream loop\n}","handlingStrategy":"type-guard","validationCode":"if (typeof Response !== \"undefined\" && !(\"body\" in new Response())) {\n  throw new Error(\"Streaming response bodies unsupported in this browser\");\n}","typeGuard":"function hasReadableBody(res: Response): res is Response & { body: ReadableStream } {\n  return res.body != null && typeof res.body.getReader === \"function\";\n}","tryCatchPattern":"try {\n  await streamIntoChunks(response);\n} catch (e) {\n  if (e instanceof Error && e.message.includes(\"No reader\")) {\n    // fall back to blob()\n  } else { throw e; }\n}","preventionTips":["Feature-check response.body before using getReader and fall back to response.blob()","Test on older Safari/WebView targets","Avoid service workers or extensions that rewrap fetch responses on this route"],"tags":["network","streaming","browser-compatibility","fetch"],"backgroundTag":"network-request-failed","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"}