{"record":{"id":"35a7d50616715124","repo":"can1357/oh-my-pi","slug":"unpacked-asar-file-label-changed-while-being","errorCode":null,"errorMessage":"Unpacked ASAR file '${label}' changed while being read","messagePattern":"Unpacked ASAR file '(.+?)' changed while being read","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"warning","filePath":"packages/utils/src/ar/asar.ts","lineNumber":174,"sourceCode":"\t\tconst stat = await file.stat().catch(() => {\n\t\t\tthrow new ArchiveError(`Unpacked ASAR file '${label}' was not found`);\n\t\t});\n\t\tif (stat.isDirectory()) {\n\t\t\tthrow new ArchiveError(`Unpacked ASAR file '${label}' is a directory`);\n\t\t}\n\t\tif (stat.size !== this.#size) {\n\t\t\tthrow new ArchiveError(\n\t\t\t\t`Unpacked ASAR file '${label}' size differs from its archive header (${stat.size} != ${this.#size} bytes)`,\n\t\t\t);\n\t\t}\n\t\tlet bytes: Uint8Array;\n\t\ttry {\n\t\t\tbytes = await file.bytes();\n\t\t} catch (error) {\n\t\t\tthrow new ArchiveError(`Failed to read unpacked ASAR file '${label}': ${describeError(error)}`);\n\t\t}\n\t\tif (bytes.byteLength !== this.#size) {\n\t\t\tthrow new ArchiveError(`Unpacked ASAR file '${label}' changed while being read`);\n\t\t}\n\t\tverifyIntegrity(bytes, this.#integrity, memberPath);\n\t\treturn bytes;\n\t}\n}\n\nasync function readAsarIndex(source: ByteSource, options: FormatReadOptions): Promise<ArchiveIndexEntry[]> {\n\tif (!Number.isSafeInteger(source.size) || source.size < ASAR_JSON_OFFSET) {\n\t\tthrow invalidAsar(\"truncated header\");\n\t}\n\n\tconst sizePickle = await source.read(0, ASAR_PICKLE_PREFIX_SIZE);\n\tif (sizePickle.byteLength !== ASAR_PICKLE_PREFIX_SIZE) {\n\t\tthrow invalidAsar(\"truncated size pickle\");\n\t}\n\tif (readUInt32LE(sizePickle, 0) !== 4) {\n\t\tthrow invalidAsar(\"invalid size pickle\");\n\t}","sourceCodeStart":156,"sourceCodeEnd":192,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/asar.ts#L156-L192","documentation":"Thrown when the bytes actually read from the unpacked sidecar differ in length from the size obtained by stat() moments earlier. This indicates the file was modified concurrently during the read, so the returned buffer would be inconsistent with the header.","triggerScenarios":"Another process wrote/truncated/replaced the .unpacked file between stat() and file.bytes(); a build watch process regenerated the file while the app was reading it.","commonSituations":"Dev hot-reload tooling touching unpacked assets at runtime; two app instances sharing one install directory; package managers or updaters swapping files while the app runs.","solutions":["Stop processes that mutate the .unpacked tree while the app reads it; retry the read once the writer finishes","Read from a stable installed copy rather than a live-build directory","Use file locking or read a snapshot copy for assets that change"],"exampleFix":"// before\nawait member.read(...)  // while `vite build --watch` writes assets\n// after\nawait buildComplete;  // wait for writer, then read\nconst bytes = await member.read(entry.size, entry.path);","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"for (let i = 0; i < 3; i++) {\n  try { return await readMember(entry); }\n  catch (e) { if (e instanceof ArchiveError && e.message.includes(\"changed while being read\")) { await Bun.sleep(50); continue; } throw e; }\n}\nthrow new Error(`file ${entry.path} is being modified concurrently`);","preventionTips":["Stop watch/build processes writing .unpacked during runtime reads","Run a single app instance against one install directory","Snapshot assets before reading if updaters may rewrite them"],"tags":["asar","race-condition","concurrency","filesystem"],"backgroundTag":"file-changed-while-reading","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}