amir20/dozzle · error · Error

No reader available from stream

Error message

No reader available from stream

What it means

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.

Solutions

  1. Update to a browser that supports ReadableStream response bodies (all modern Chrome/Firefox/Safari 10.1+)
  2. Disable fetch-intercepting extensions or service workers and retry
  3. Ensure the analytics request is same-origin/CORS-clean so the body is not opaque
  4. Add a fallback to consume the response as a blob/arrayBuffer when getReader is unavailable

Example fix

// before
const reader = response.body?.getReader();
if (!reader) throw new Error("No reader available from stream");
// after
if (!response.body) {
  const blob = await response.blob();
  chunks.push(new Uint8Array(await blob.arrayBuffer()));
} else {
  const reader = response.body.getReader();
  // ...stream loop
}
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof Response !== "undefined" && !("body" in new Response())) {
  throw new Error("Streaming response bodies unsupported in this browser");
}

Type guard

function hasReadableBody(res: Response): res is Response & { body: ReadableStream } {
  return res.body != null && typeof res.body.getReader === "function";
}

Try / catch

try {
  await streamIntoChunks(response);
} catch (e) {
  if (e instanceof Error && e.message.includes("No reader")) {
    // fall back to blob()
  } else { throw e; }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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

Appendix: source

Thrown at assets/components/LogViewer/LogAnalytics.vue:148

  `/api/hosts/${container.host}/containers/${container.id}/logs?stdout=1&stderr=1&everything&jsonOnly`,
);

const [{ useDuckDB }, response] = await Promise.all([import(`@/composable/duckdb`), fetch(url)]);

if (!response.ok) {
  console.log("error fetching logs from", url);
  throw new Error(`Failed to fetch logs: ${response.statusText}`);
}

const { db, conn } = await useDuckDB();
const empty = await conn.query<Record<string, any>>(`SELECT 1 LIMIT 0`);

onMounted(async () => {
  try {
    state.value = "downloading";

    const reader = response.body?.getReader();
    if (!reader) throw new Error("No reader available from stream");

    const chunks: Uint8Array[] = [];
    bytes.value = 0;

    while (true) {
      const { done, value } = await reader.read();
      if (done) break;
      chunks.push(value);
      bytes.value += value.length;
    }

    const arrayBuffer = new Uint8Array(bytes.value);
    let position = 0;
    for (const chunk of chunks) {
      arrayBuffer.set(chunk, position);
      position += chunk.length;
    }

View on GitHub (pinned to d9463cbe21)