{"record":{"id":"a74a5448f7725177","repo":"oven-sh/bun","slug":"no-zip-eocd-in-url","errorCode":null,"errorMessage":"no zip EOCD in ${url}","messagePattern":"no zip EOCD in (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/binary-size.ts","lineNumber":339,"sourceCode":"// (`<triplet>/` + `<triplet>/bun[.exe]`) zips.\nasync function zipBinarySize(url: string): Promise<number> {\n  const head = await fetch(url, { method: \"HEAD\" });\n  if (!head.ok) throw new Error(`HEAD ${url}: ${head.status}`);\n  const total = Number(head.headers.get(\"content-length\"));\n  const tail = Math.min(65536, total);\n  const res = await fetch(url, { headers: { Range: `bytes=${total - tail}-${total - 1}` } });\n  if (!res.ok) throw new Error(`Range ${url}: ${res.status}`);\n  const buf = new Uint8Array(await res.arrayBuffer());\n  const dv = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);\n\n  let eocd = -1;\n  for (let i = buf.length - 22; i >= Math.max(0, buf.length - 22 - 65535); i--) {\n    if (dv.getUint32(i, true) === 0x06054b50) {\n      eocd = i;\n      break;\n    }\n  }\n  if (eocd < 0) throw new Error(`no zip EOCD in ${url}`);\n\n  let p = dv.getUint32(eocd + 16, true) - (total - tail);\n  if (p < 0) throw new Error(`zip central directory not within tail for ${url}`);\n\n  let size = 0;\n  while (p + 46 <= eocd && dv.getUint32(p, true) === 0x02014b50) {\n    const uncompressed = dv.getUint32(p + 24, true);\n    const nameLen = dv.getUint16(p + 28, true);\n    const name = new TextDecoder().decode(buf.subarray(p + 46, p + 46 + nameLen));\n    // The binary is the only non-directory entry; take the largest in case the\n    // zip ever grows extra metadata files.\n    if (!name.endsWith(\"/\") && uncompressed > size) size = uncompressed;\n    p += 46 + nameLen + dv.getUint16(p + 30, true) + dv.getUint16(p + 32, true);\n  }\n  if (size === 0) throw new Error(`no file entry in ${url}`);\n  return size;\n}\n","sourceCodeStart":321,"sourceCodeEnd":357,"githubUrl":"https://github.com/oven-sh/bun/blob/8c5296ac459e8252d3cd702f3fbcbb0c249d95d5/scripts/binary-size.ts#L321-L357","documentation":"zipBinarySize() scans the fetched 64 KB tail backwards for the zip End-Of-Central-Directory signature 0x06054b50 (covering the maximum 64 KB zip comment). This error means no EOCD was found — the tail does not look like the end of any zip file.","triggerScenarios":"The response body is not actually a zip — an HTML/XML error page returned with a 2xx status from the CDN; content-length lied so the tail window misses the real end; or the object is truncated.","commonSituations":"CDN soft-error pages on stale URLs; an asset replaced by a redirect or pointer file; proxies returning partial content with 200.","solutions":["curl the URL and inspect content-type plus the first bytes (a zip starts with PK\\x03\\x04)","Verify the URL came from the release assets list and matches isBinaryZip","Download the file fully and run unzip -l to confirm it is a valid zip","If asset naming changed, update the isBinaryZip filter"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"// cheap sanity check before trusting the tail: a zip starts with PK\\x03\\x04\nconst head = await fetch(url, { headers: { Range: \"bytes=0-3\" } });\nconst magic = new Uint8Array(await head.arrayBuffer());\nif (magic[0] !== 0x50 || magic[1] !== 0x4b) throw new Error(`not a zip: ${url}`);","typeGuard":null,"tryCatchPattern":"try { return await zipBinarySize(url); }\ncatch (e) {\n  if (/no zip EOCD/.test(e.message)) {\n    return sizeFromFullZip(new Uint8Array(await (await fetch(url)).arrayBuffer()));\n  }\n  throw e;\n}","preventionTips":["Check the zip magic bytes before parsing a tail window","Do not assume 2xx means zip bytes — verify content-type and length against expectations"],"tags":["zip","parsing","binary-size","data-corruption"],"backgroundTag":null,"analyzedSha":"8c5296ac459e8252d3cd702f3fbcbb0c249d95d5","analyzedAt":"2026-08-16T08:01:58.794Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}