{"record":{"id":"c6c0f2a8288bb7f5","repo":"can1357/oh-my-pi","slug":"remote-archive-did-not-report-a-valid-size-in-cont","errorCode":null,"errorMessage":"Remote archive did not report a valid size in Content-Range","messagePattern":"Remote archive did not report a valid size in Content-Range","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/source.ts","lineNumber":115,"sourceCode":"\t\t\t\t`Remote archive is too large to buffer without range support (${declared} > ${cap} bytes)`,\n\t\t\t);\n\t\t}\n\t\tconst bytes = new Uint8Array(await probe.arrayBuffer());\n\t\tif (bytes.byteLength > cap) {\n\t\t\tthrow new ArchiveError(`Remote archive is too large to buffer without range support (> ${cap} bytes)`);\n\t\t}\n\t\treturn memoryByteSource(bytes);\n\t}\n\tif (probe.status !== 206) {\n\t\tawait probe.body?.cancel();\n\t\tthrow new ArchiveError(`Remote archive request failed (HTTP ${probe.status})`);\n\t}\n\tawait probe.body?.cancel();\n\t// `Content-Range: bytes 0-0/12345` carries the total size.\n\tconst contentRange = probe.headers.get(\"content-range\");\n\tconst total = contentRange ? Number(/\\/(\\d+)$/.exec(contentRange)?.[1]) : Number.NaN;\n\tif (!Number.isSafeInteger(total) || total < 0) {\n\t\tthrow new ArchiveError(\"Remote archive did not report a valid size in Content-Range\");\n\t}\n\treturn {\n\t\tsize: total,\n\t\tasync read(start, end) {\n\t\t\tassertValidRange(start, end);\n\t\t\tif (start === end) return new Uint8Array(0);\n\t\t\tconst response = await doFetch(url, {\n\t\t\t\theaders: { ...options.headers, range: `bytes=${start}-${end - 1}` },\n\t\t\t});\n\t\t\tif (response.status !== 206) {\n\t\t\t\tawait response.body?.cancel();\n\t\t\t\tthrow new ArchiveError(`Remote archive range request failed (HTTP ${response.status})`);\n\t\t\t}\n\t\t\tconst bytes = new Uint8Array(await response.arrayBuffer());\n\t\t\tif (bytes.byteLength !== end - start) {\n\t\t\t\tthrow new ArchiveError(\"Invalid archive: truncated data\");\n\t\t\t}\n\t\t\treturn bytes;","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/source.ts#L97-L133","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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)","Check for stripping proxies/CDNs in front of the archive and bypass or reconfigure them","Fetch Content-Length via a HEAD request and wrap the bytes yourself with memoryByteSource if the archive is small enough to buffer","Download to disk and use fileByteSource() as a fallback when the remote server's range implementation is broken"],"exampleFix":"// before: server returns 206 without Content-Range — throws\nconst src = await httpByteSource(\"https://odd-server.example/a.tar\");\n// after: learn size via HEAD, buffer locally if within limits\nconst head = await fetch(url, { method: \"HEAD\" });\nconst size = Number(head.headers.get(\"content-length\"));\nconst src = size < 256 * 1024 * 1024\n\t? memoryByteSource(new Uint8Array(await (await fetch(url)).arrayBuffer()))\n\t: await httpByteSource(url);","handlingStrategy":"fallback","validationCode":"const res = await fetch(url, { headers: { range: \"bytes=0-0\" } });\nif (res.status === 206 && !res.headers.get(\"content-range\")) {\n\tconsole.warn(\"server sends 206 without Content-Range; use local download fallback\");\n}","typeGuard":null,"tryCatchPattern":"let src;\ntry {\n\tsrc = await httpByteSource(url, { headers });\n} catch (err) {\n\tif (err instanceof ArchiveError && err.message.includes(\"Content-Range\")) {\n\t\tsrc = await downloadToDiskAndOpen(url); // fileByteSource fallback\n\t} else throw err;\n}","preventionTips":["Test the server once with curl -r 0-0 -i and confirm a Content-Range header appears on the 206","Avoid nonstandard file servers/proxies in the archive path, or configure them to emit RFC 7233-compliant Content-Range","Keep a download-to-disk fallback path in code for hosts with broken range implementations","Prefer mainstream object stores/CDNs that implement range responses correctly"],"tags":["http","headers","remote-archive","range-requests"],"backgroundTag":"missing-content-range-header","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}