{"record":{"id":"fc2aad90a8cf4d5f","repo":"can1357/oh-my-pi","slug":"invalid-rpm-package-truncated-what","errorCode":null,"errorMessage":"Invalid RPM package: truncated ${what}","messagePattern":"Invalid RPM package: truncated (.+?)","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/rpm.ts","lineNumber":48,"sourceCode":"\ttotalSize: number;\n}\n\ninterface RpmMetadata {\n\tname?: string;\n\tversion?: string;\n\tpayloadFormat?: string;\n\tpayloadCompressor?: string;\n\tpayloadFlags?: string;\n}\n\nfunction align(value: number, alignment: number): number {\n\tconst remainder = value % alignment;\n\treturn remainder === 0 ? value : value + alignment - remainder;\n}\n\nasync function readExact(source: ByteSource, start: number, end: number, what: string): Promise<Uint8Array> {\n\tif (!Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start < 0 || end < start || end > source.size) {\n\t\tthrow new ArchiveError(`Invalid RPM package: truncated ${what}`);\n\t}\n\tconst bytes = await source.read(start, end);\n\tif (bytes.byteLength !== end - start) throw new ArchiveError(`Invalid RPM package: truncated ${what}`);\n\treturn bytes;\n}\n\nfunction parseHeaderIntro(bytes: Uint8Array, options: FormatReadOptions, what: string): HeaderIntro {\n\tif (bytes.byteLength !== RPM_HEADER_INTRO_SIZE || readUInt32BE(bytes, 0) !== RPM_HEADER_MAGIC) {\n\t\tthrow new ArchiveError(`Invalid RPM package: corrupt ${what} header magic`);\n\t}\n\tfor (let offset = 4; offset < 8; offset++) {\n\t\tif (bytes[offset] !== 0) throw new ArchiveError(`Invalid RPM package: corrupt ${what} header reserved bytes`);\n\t}\n\tconst indexCount = readUInt32BE(bytes, 8);\n\tconst dataSize = readUInt32BE(bytes, 12);\n\tassertEntryCount(indexCount, options.limits);\n\tconst indexSize = indexCount * RPM_INDEX_ENTRY_SIZE;\n\tconst bodySize = indexSize + dataSize;","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/rpm.ts#L30-L66","documentation":"ArchiveError thrown by the RPM reader's readExact() helper when the requested byte range [start,end) is not a valid range within the source (non-safe integers, negative start, end<start, or end beyond source.size). This is the first of two truncation checks — it validates the range arithmetically before any I/O.","triggerScenarios":"Reading RPM structures (initial lead, signature header, main intro, body, payload) where computed offsets exceed the actual file size — i.e. the file is smaller than the RPM format requires at that point.","commonSituations":"Truncated downloads of .rpm files, HTTP transfers cut short, passing a non-RPM or empty file to the RPM reader, or CDN/proxy stripping bytes.","solutions":["Re-download or re-fetch the .rpm file and verify size/checksum against the repository metadata","Check that the byte source (file/blob) is fully written before parsing","Confirm the file actually is an RPM (starts with the ar '!<arch>' / RPM lead) before parsing"],"exampleFix":"// before\nconst rpm = await readRpmArchive(await Bun.file(partialPath).arrayBuffer());\n// after\nconst buf = await Bun.file(path).arrayBuffer();\nif (buf.byteLength < 96) throw new Error(`RPM too small: ${buf.byteLength} bytes`);\nconst rpm = await readRpmArchive(buf);","handlingStrategy":"validation","validationCode":"const size = Bun.file(path).size;\nif (size < 96) throw new Error(`rpm too small (${size} bytes)`); // minimum: lead + headers","typeGuard":"null","tryCatchPattern":"try {\n  const rpm = await readRpmArchive(buf);\n} catch (err) {\n  if (err instanceof ArchiveError && err.message.includes('truncated')) {\n    // surface 'download incomplete' to the user\n  } else throw err;\n}","preventionTips":["Verify downloaded file size against repository metadata before parsing","Buffer the whole source before parsing instead of streaming partial data","Confirm the file extension/lead actually indicate an RPM"],"tags":["rpm","archive","truncated-file"],"backgroundTag":"archive-truncated","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}