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
- 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
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
- 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
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)