can1357/oh-my-pi · error · ArchiveError

Remote archive range request failed (HTTP ${response.status}

Error message

Remote archive range request failed (HTTP ${response.status})

What it means

The ByteSource returned by httpByteSource() fulfills each read(start, end) with a ranged GET expecting a 206 Partial Content response. This throw fires when a later range read returns any other status — the server that supported the probe may have started rejecting requests, or per-request auth/URL problems surfaced mid-stream. The response body is cancelled before throwing.

Source

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

	}
	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;
		},
	};
}

/** Options for {@link cachingByteSource}. */
export interface CachingByteSourceOptions {
	/** Cache block size in bytes. Default 256 KiB. */
	blockSize?: number;
	/** Max cached blocks. Default 64 (16 MiB at the default block size). */
	maxBlocks?: number;
}

View on GitHub (pinned to 9690622007)

Solutions

  1. Implement retry with fresh credentials/URL for each read — re-create httpByteSource with a newly signed URL when reads start failing
  2. Add a retry wrapper around read() with backoff for 429/5xx statuses
  3. Reduce concurrent range reads (or wrap with cachingByteSource) to avoid rate limiting
  4. Fall back to downloading the whole archive to disk (fileByteSource) when the remote is unstable

Example fix

// before: single long-lived source dies when the presigned URL expires
const src = await httpByteSource(signedUrl);
await src.read(0, 512); // ... later 403
// after: refresh the URL per read session
async function readRange(url, start, end) {
	const src = await httpByteSource(await refreshSignedUrl(url));
	return src.read(start, end);
}
Defensive patterns

Strategy: retry

Try / catch

async function readResilient(src, start, end, attempts = 3) {
	for (let i = 0; ; i++) {
		try {
			return await src.read(start, end);
		} catch (err) {
			if (i >= attempts || !(err instanceof ArchiveError) || !/HTTP \d+/.test(err.message)) throw err;
			await Bun.sleep(2 ** i * 250);
		}
	}
}

Prevention

When it happens

Trigger: Any ByteSource.read(start, end) on the httpByteSource source where the ranged fetch returns non-206: presigned URL expired between probe and read (403), the object was deleted mid-session (404), rate limiting (429), transient 5xx, or a proxy that forwards the initial request but rejects subsequent ranged ones.

Common situations: Long-running extraction sessions over expiring S3 presigned URLs; load balancers routing reads to servers lacking the object; throttling under many concurrent small range reads; CDN misconfiguration that caches the probe but 4xx's range requests.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


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