{"record":{"id":"c7c8096c0eb74bb2","repo":"can1357/oh-my-pi","slug":"asar-member-formatarchivepathforerror-memberpat-c7c809","errorCode":null,"errorMessage":"ASAR member '${formatArchivePathForError(memberPath)}' is truncated","messagePattern":"ASAR member '(.+?)' is truncated","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/asar.ts","lineNumber":129,"sourceCode":"\t\tthis.#size = size;\n\t\tthis.#integrity = integrity;\n\t}\n\n\tasync read(size: number, memberPath: string): Promise<Uint8Array> {\n\t\tif (size !== this.#size) {\n\t\t\tthrow new ArchiveError(`ASAR member '${formatArchivePathForError(memberPath)}' has an inconsistent size`);\n\t\t}\n\t\tlet bytes: Uint8Array;\n\t\ttry {\n\t\t\tbytes = await this.#source.read(this.#offset, this.#offset + this.#size);\n\t\t} catch (error) {\n\t\t\tif (error instanceof ArchiveError) throw error;\n\t\t\tthrow new ArchiveError(\n\t\t\t\t`Failed to read ASAR member '${formatArchivePathForError(memberPath)}': ${describeError(error)}`,\n\t\t\t);\n\t\t}\n\t\tif (bytes.byteLength !== this.#size) {\n\t\t\tthrow new ArchiveError(`ASAR member '${formatArchivePathForError(memberPath)}' is truncated`);\n\t\t}\n\t\tverifyIntegrity(bytes, this.#integrity, memberPath);\n\t\treturn bytes;\n\t}\n}\n\nclass UnpackedAsarMemberSource implements MemberSource {\n\treadonly #filePath?: string;\n\treadonly #size: number;\n\treadonly #integrity?: AsarIntegrity;\n\n\tconstructor(filePath: string | undefined, size: number, integrity: AsarIntegrity | undefined) {\n\t\tthis.#filePath = filePath;\n\t\tthis.#size = size;\n\t\tthis.#integrity = integrity;\n\t}\n\n\tasync read(size: number, memberPath: string): Promise<Uint8Array> {","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/asar.ts#L111-L147","documentation":"After a successful source read, the reader checks that the number of bytes actually returned equals the member's header-recorded size. Fewer bytes means the archive data region ends before this member does — the ASAR is truncated — so the reader refuses to hand back partial data and skip the integrity check.","triggerScenarios":"PackedAsarMemberSource.read gets bytes whose byteLength < the header-recorded #size — the source's byte range extends past the end of the backing file/buffer, i.e. the ASAR file was truncated after the header was written (and the header-consistency check at read(size,...) passed).","commonSituations":"Incomplete downloads or interrupted copies of the .asar file; disk-full during packaging; sync tools that partially uploaded the archive; reading while the file is still being written.","solutions":["Re-download or re-copy the complete ASAR and compare byte length against the original's known size/hash.","Check available disk space and that no writer still holds the file open mid-write before reading.","Validate whole-file integrity (size vs. header's expected total length) before member reads and fail fast with your own clearer message.","Repackage with the official asar tool if the packaging process itself was interrupted."],"exampleFix":"// before\nconst bytes = await member.read(size, path); // throws \"is truncated\"\n// after\nconst stat = await fs.stat(asarPath);\nif (stat.size < expectedTotalFromHeader(asarPath)) {\n  throw new Error(\"ASAR file incomplete, re-downloading\");\n}\nconst bytes = await member.read(size, path);","handlingStrategy":"validation","validationCode":"import * as fs from \"node:fs/promises\";\nasync function assertAsarComplete(asarPath: string, headerTotalSize: number): Promise<void> {\n\tconst stat = await fs.stat(asarPath);\n\tif (stat.size < headerTotalSize) {\n\t\tthrow new Error(`ASAR truncated: ${stat.size} bytes on disk, header expects ${headerTotalSize}`);\n\t}\n}","typeGuard":"function isCompleteAsar(statSize: number, headerTotalSize: number): boolean {\n\treturn statSize >= headerTotalSize;\n}","tryCatchPattern":"try {\n\tconst bytes = await member.read(size, path);\n} catch (e) {\n\tif (e instanceof ArchiveError && e.message.includes(\"is truncated\")) {\n\t\tawait reDownloadAsar(sourceUrl, asarPath); // truncated file cannot be repaired in place\n\t\treturn readMemberRetry(path);\n\t}\n\tthrow e;\n}","preventionTips":["Compare the on-disk file size against the ASAR header's total length before any member read.","Verify download completion (content-length vs. bytes received, or whole-file hash) before processing.","Avoid reading archives while they are still being written or synced.","Check disk space on the machine performing packaging/copying."],"tags":["archive","asar","truncated-file","io"],"backgroundTag":"truncated-archive","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}