can1357/oh-my-pi · error · ArchiveError

Remote archive did not report a valid size in Content-Range

Error message

Remote archive did not report a valid size in Content-Range

What it means

After a successful 206 probe, httpByteSource() derives the archive's total size from the 'Content-Range: bytes 0-0/<total>' header. This throw fires when the header is missing, malformed, or its trailing total is not a non-negative safe integer. The library needs the total size to validate subsequent ranges and to report ByteSource.size, so it refuses to proceed without it.

Source

Thrown at packages/utils/src/ar/source.ts:115

				`Remote archive is too large to buffer without range support (${declared} > ${cap} bytes)`,
			);
		}
		const bytes = new Uint8Array(await probe.arrayBuffer());
		if (bytes.byteLength > cap) {
			throw new ArchiveError(`Remote archive is too large to buffer without range support (> ${cap} bytes)`);
		}
		return memoryByteSource(bytes);
	}
	if (probe.status !== 206) {
		await probe.body?.cancel();
		throw new ArchiveError(`Remote archive request failed (HTTP ${probe.status})`);
	}
	await probe.body?.cancel();
	// `Content-Range: bytes 0-0/12345` carries the total size.
	const contentRange = probe.headers.get("content-range");
	const total = contentRange ? Number(/\/(\d+)$/.exec(contentRange)?.[1]) : Number.NaN;
	if (!Number.isSafeInteger(total) || total < 0) {
		throw new ArchiveError("Remote archive did not report a valid size in Content-Range");
	}
	return {
		size: total,
		async read(start, end) {
			assertValidRange(start, end);
			if (start === end) return new Uint8Array(0);
			const response = await doFetch(url, {
				headers: { ...options.headers, range: `bytes=${start}-${end - 1}` },
			});
			if (response.status !== 206) {
				await response.body?.cancel();
				throw new ArchiveError(`Remote archive range request failed (HTTP ${response.status})`);
			}
			const bytes = new Uint8Array(await response.arrayBuffer());
			if (bytes.byteLength !== end - start) {
				throw new ArchiveError("Invalid archive: truncated data");
			}
			return bytes;

View on GitHub (pinned to 9690622007)

Solutions

  1. Fix or replace the server so a 206 response includes 'Content-Range: bytes start-end/total' (required by RFC 7233 for 206 single-range responses)
  2. Check for stripping proxies/CDNs in front of the archive and bypass or reconfigure them
  3. Fetch Content-Length via a HEAD request and wrap the bytes yourself with memoryByteSource if the archive is small enough to buffer
  4. Download to disk and use fileByteSource() as a fallback when the remote server's range implementation is broken

Example fix

// before: server returns 206 without Content-Range — throws
const src = await httpByteSource("https://odd-server.example/a.tar");
// after: learn size via HEAD, buffer locally if within limits
const head = await fetch(url, { method: "HEAD" });
const size = Number(head.headers.get("content-length"));
const src = size < 256 * 1024 * 1024
	? memoryByteSource(new Uint8Array(await (await fetch(url)).arrayBuffer()))
	: await httpByteSource(url);
Defensive patterns

Strategy: fallback

Validate before calling

const res = await fetch(url, { headers: { range: "bytes=0-0" } });
if (res.status === 206 && !res.headers.get("content-range")) {
	console.warn("server sends 206 without Content-Range; use local download fallback");
}

Try / catch

let src;
try {
	src = await httpByteSource(url, { headers });
} catch (err) {
	if (err instanceof ArchiveError && err.message.includes("Content-Range")) {
		src = await downloadToDiskAndOpen(url); // fileByteSource fallback
	} else throw err;
}

Prevention

When it happens

Trigger: Calling httpByteSource() against a server that returns 206 but omits Content-Range, sends a malformed value (e.g. 'bytes */*' or missing the /total suffix), or sends a non-integer/negative total — the regex /\/(\d+)$/ then fails to parse and total is NaN.

Common situations: Nonstandard or hand-rolled HTTP file servers that honor Range with 206 but omit the Content-Range header; proxies that strip response headers; multipart/byte-range caches that rewrite headers; HTTP/2 intermediaries dropping custom header casing in exotic setups.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/c6c0f2a8288bb7f5. Report an issue: GitHub.