can1357/oh-my-pi · warning · ArchiveError

Unpacked ASAR file '${label}' changed while being read

Error message

Unpacked ASAR file '${label}' changed while being read

What it means

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.

Source

Thrown at packages/utils/src/ar/asar.ts:174

		const stat = await file.stat().catch(() => {
			throw new ArchiveError(`Unpacked ASAR file '${label}' was not found`);
		});
		if (stat.isDirectory()) {
			throw new ArchiveError(`Unpacked ASAR file '${label}' is a directory`);
		}
		if (stat.size !== this.#size) {
			throw new ArchiveError(
				`Unpacked ASAR file '${label}' size differs from its archive header (${stat.size} != ${this.#size} bytes)`,
			);
		}
		let bytes: Uint8Array;
		try {
			bytes = await file.bytes();
		} catch (error) {
			throw new ArchiveError(`Failed to read unpacked ASAR file '${label}': ${describeError(error)}`);
		}
		if (bytes.byteLength !== this.#size) {
			throw new ArchiveError(`Unpacked ASAR file '${label}' changed while being read`);
		}
		verifyIntegrity(bytes, this.#integrity, memberPath);
		return bytes;
	}
}

async function readAsarIndex(source: ByteSource, options: FormatReadOptions): Promise<ArchiveIndexEntry[]> {
	if (!Number.isSafeInteger(source.size) || source.size < ASAR_JSON_OFFSET) {
		throw invalidAsar("truncated header");
	}

	const sizePickle = await source.read(0, ASAR_PICKLE_PREFIX_SIZE);
	if (sizePickle.byteLength !== ASAR_PICKLE_PREFIX_SIZE) {
		throw invalidAsar("truncated size pickle");
	}
	if (readUInt32LE(sizePickle, 0) !== 4) {
		throw invalidAsar("invalid size pickle");
	}

View on GitHub (pinned to 9690622007)

Solutions

  1. Stop processes that mutate the .unpacked tree while the app reads it; retry the read once the writer finishes
  2. Read from a stable installed copy rather than a live-build directory
  3. Use file locking or read a snapshot copy for assets that change

Example fix

// before
await member.read(...)  // while `vite build --watch` writes assets
// after
await buildComplete;  // wait for writer, then read
const bytes = await member.read(entry.size, entry.path);
Defensive patterns

Strategy: retry

Try / catch

for (let i = 0; i < 3; i++) {
  try { return await readMember(entry); }
  catch (e) { if (e instanceof ArchiveError && e.message.includes("changed while being read")) { await Bun.sleep(50); continue; } throw e; }
}
throw new Error(`file ${entry.path} is being modified concurrently`);

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/35a7d50616715124. Report an issue: GitHub.