different-ai/openwork · error

size-mismatch

size-mismatch

Error message

Downloaded data changed while it was being saved.

What it means

While copying bytes from the staging file to the final destination, downloadBinaryToPath throws 'size-mismatch' if a read returns 0 bytes before `bytes` (the count just downloaded) have been copied. This means the staging file shrank or became unreadable between download and promotion — the downloaded data changed while it was being saved.

Source

Thrown at apps/desktop/electron/binary-transfer.mjs:333

    // symlink), and prove by device and inode that the created file resides
    // inside the authorized root before a single byte is written to it.
    await resolveAuthorizedPath(input?.destinationPath, options?.authorizedRoots);
    try {
      destinationFile = await open(destinationPath, "wx");
    } catch (error) {
      if (error?.code === "EEXIST") {
        throw transferError("Download destination already exists.", "destination-exists");
      }
      throw error;
    }
    await verifyOpenFileWithinRoot(destinationFile, destinationPath, destination.rootRealPath, "Download destination");
    const buffer = Buffer.allocUnsafe(1024 * 1024);
    let position = 0;
    while (position < bytes) {
      signal?.throwIfAborted();
      const { bytesRead } = await stagingFile.read(buffer, 0, Math.min(buffer.length, bytes - position), position);
      if (bytesRead === 0) {
        throw transferError("Downloaded data changed while it was being saved.", "size-mismatch");
      }
      await writeAll(destinationFile, buffer.subarray(0, bytesRead));
      position += bytesRead;
    }
    await destinationFile.sync();
    await destinationFile.close();
    destinationFile = undefined;
    return { ...responseMetadata(response), path: destinationPath, bytes };
  } catch (error) {
    // Cleanup goes through the verified handle, never through the path, so a
    // swapped parent cannot turn cleanup into an out-of-workspace deletion.
    if (destinationFile) {
      await destinationFile.truncate(0).catch(() => undefined);
      await destinationFile.close().catch(() => undefined);
    }
    throw error;
  } finally {
    await stagingFile?.close().catch(() => undefined);

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Simply retry the whole download — this is detected corruption, and a fresh run to a fresh staging file is the fix.
  2. Ensure nothing purges the staging directory during downloads (antivirus exclusions, tmpwatch/systemd-tmpfiles settings).
  3. Check free disk space on the staging volume; ENOSPC can truncate files.
  4. Avoid sharing one staging/temp directory across concurrent downloads.

Example fix

// before
await downloadBinaryToPath({ url, destinationPath });
// after
import { transferErrorShape } from "./binary-transfer.mjs";
try {
  await downloadBinaryToPath({ url, destinationPath });
} catch (e) {
  if (e.code === "size-mismatch") await downloadBinaryToPath({ url, destinationPath }); // one clean retry
  else throw e;
}
Defensive patterns

Strategy: retry

Validate before calling

import { statfs } from "node:fs/promises";
const { bavail, bsize } = await statfs(path.dirname(destinationPath));
if (bavail * bsize < expectedBytes * 2) throw new Error("Insufficient free space for staging plus destination");

Try / catch

try {
  await downloadBinaryToPath({ url, destinationPath });
} catch (e) {
  if (e?.code === "size-mismatch") {
    // staging file was truncated mid-copy: one clean retry on a fresh staging file
    return downloadBinaryToPath({ url, destinationPath });
  }
  throw e;
}

Prevention

When it happens

Trigger: During the staging-to-destination copy loop, stagingFile.read returns bytesRead === 0 while position < bytes, i.e. the staging file is shorter than the byte count recorded during streaming.

Common situations: Another process/user truncating or deleting the staging file mid-transfer (cleanup daemon, antivirus, temp-file sweeper); disk-full or fs errors surfacing as short reads; racing downloads sharing a temp directory.

Related errors


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/54d328738ea162f9. Report an issue: GitHub.