{"record":{"id":"7c5fbb33761dc1b7","repo":"can1357/oh-my-pi","slug":"invalid-rpm-package-truncated-signature-header","errorCode":null,"errorMessage":"Invalid RPM package: truncated signature header","messagePattern":"Invalid RPM package: truncated signature header","errorType":"validation","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/rpm.ts","lineNumber":273,"sourceCode":"\tconst leadNameEnd = initial.subarray(10, 76).indexOf(0);\n\tconst leadNameBytes = initial.subarray(10, leadNameEnd < 0 ? 76 : 10 + leadNameEnd);\n\tlet leadName = \"unknown package\";\n\ttry {\n\t\tconst decoded = new TextDecoder(\"utf-8\", { fatal: true }).decode(leadNameBytes);\n\t\tif (decoded) leadName = decoded;\n\t} catch {}\n\n\tconst signatureIntro = parseHeaderIntro(initial.subarray(RPM_LEAD_SIZE), options, \"signature\");\n\tconst signatureEnd = RPM_LEAD_SIZE + signatureIntro.totalSize;\n\tconst mainHeaderOffset = align(signatureEnd, 8);\n\tconst signatureBodyAndPadding = await readExact(\n\t\tsource,\n\t\tRPM_LEAD_SIZE + RPM_HEADER_INTRO_SIZE,\n\t\tmainHeaderOffset,\n\t\t\"signature header\",\n\t);\n\tif (signatureBodyAndPadding.byteLength < signatureIntro.bodySize) {\n\t\tthrow new ArchiveError(\"Invalid RPM package: truncated signature header\");\n\t}\n\tvalidateHeaderBody(signatureBodyAndPadding.subarray(0, signatureIntro.bodySize), signatureIntro, \"signature\");\n\tfor (let offset = signatureIntro.bodySize; offset < signatureBodyAndPadding.byteLength; offset++) {\n\t\tif (signatureBodyAndPadding[offset] !== 0) {\n\t\t\tthrow new ArchiveError(\"Invalid RPM package: non-zero signature alignment padding\");\n\t\t}\n\t}\n\n\tconst mainIntroBytes = await readExact(\n\t\tsource,\n\t\tmainHeaderOffset,\n\t\tmainHeaderOffset + RPM_HEADER_INTRO_SIZE,\n\t\t\"main header intro\",\n\t);\n\tconst mainIntro = parseHeaderIntro(mainIntroBytes, options, \"main\");\n\tassertIndexSize(signatureIntro.totalSize + mainIntro.totalSize, options.limits, \"RPM headers\");\n\tconst mainBodyOffset = mainHeaderOffset + RPM_HEADER_INTRO_SIZE;\n\tconst mainBody = await readExact(source, mainBodyOffset, mainBodyOffset + mainIntro.bodySize, \"main header\");","sourceCodeStart":255,"sourceCodeEnd":291,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/rpm.ts#L255-L291","documentation":"After the lead, the reader loads the signature header body and requires at least signatureIntro.bodySize bytes of it to be present. Fewer bytes than the signature header declares means the file ends early — the signature region is truncated — so parsing is aborted before reading the main header.","triggerScenarios":"readExact succeeded on lead+intro but the subsequent read of the signature header returned fewer bytes than the signature intro's declared bodySize (indexCount*16 + dataSize); raised before validateHeaderBody.","commonSituations":"Truncated downloads/uploads, files clipped by transfer size limits, storage corruption near the end of the file, or streams that were closed before the whole package was written.","solutions":["Re-download the RPM and verify its checksum against the repository digest.","Compare the local file size against the mirror's content-length; a smaller size confirms truncation.","Check for proxy/transfer limits (reverse proxies, S3 multipart aborts) that cut the download short and retry.","Validate with rpm -qp <file>; if the system rpm also rejects it, the file is definitively corrupt."],"exampleFix":"// before: trusting a partially streamed file\nawait Bun.write(dest, await resp.arrayBuffer()); // may silently truncate on abort\n// after: verify completeness\nconst expected = Number(resp.headers.get('content-length'));\nconst buf = await resp.arrayBuffer();\nif (buf.byteLength !== expected) throw new Error('download truncated');\nawait Bun.write(dest, buf);","handlingStrategy":"validation","validationCode":"// Ensure the file is at least large enough for lead + signature header before parsing\nconst size = await Bun.file(path).size;\nif (size < 96 + 16) throw new Error(`RPM ${path} too small (${size}B) — truncated download`);","typeGuard":null,"tryCatchPattern":"try {\n  return await readRpm(path);\n} catch (err) {\n  if (err instanceof ArchiveError && err.message.includes('truncated signature header')) {\n    throw new Error('RPM truncated mid-signature-header; re-download and verify checksum');\n  }\n  throw err;\n}","preventionTips":["Enforce content-length checks on every download before persisting the artifact.","Watch for proxies/CDNs that cut large files; enable resume + final-size verification.","Checksum-verify RPMs at the edge of your pipeline, before any parsing."],"tags":["rpm","archive","truncated-file","corruption"],"backgroundTag":"truncated-archive-header","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}