immich-app/immich · error
Unable to complete move. File checksum mismatch
Error message
Unable to complete move. File checksum mismatch: ${newChecksum.toString('base64')} !== ${checksum.toString('base64')} What it means
When storage-template hash verification is enabled and the move is for an asset, the copied file's SHA-1 checksum is compared to the checksum stored for the asset. Any difference fails verification, and this warning is logged with both base64 checksums before the caller rolls back the copy.
Solutions
- Verify the stored checksum against the ORIGINAL file; if the original also mismatches, re-generate checksums
- Check disk health (SMART) on both source and destination
- Re-copy the file and re-run verification
- Temporarily disable hashVerificationEnabled to isolate whether size or hash check fails, then re-enable
Defensive patterns
Strategy: retry
Validate before calling
const expected = assetInfo.checksum.toString('base64');
const actual = (await hashFile(newPath)).toString('base64');
if (expected !== actual) await fs.promises.unlink(newPath); // roll back before proceeding Try / catch
if (!(await verifyNewPathContentsMatchesExpected(oldPath, newPath, assetInfo))) {
await storage.unlink(newPath);
logger.warn(`hash mismatch expected=${expected}`);
return moveFailed();
} Prevention
- Run SMART/disk health checks on storage volumes
- Keep hashVerificationEnabled true in production
- Recompute asset checksums if stored values are suspected stale
- Avoid concurrent writes to the same asset during moves
When it happens
Trigger: moveFile -> verifyNewPathContentsMatchesExpected with assetInfo present and config.storageTemplate.hashVerificationEnabled=true; cryptoRepository.hashFile(newPath) differs from assetInfo.checksum — copy corrupted data or the stored checksum is stale/wrong.
Common situations: Bit rot or corruption during copy on failing disks; asset checksum recorded before a later metadata-side change; mismatched checksum algorithm after version migration; file mutated by another process between copy and hash.
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
Related errors
- Skipping move due to file size mismatch
- Unable to complete move. File size mismatch
- Attempted to clear cache, but rmtree is not safe on this…
- Failed to create
- Failed to read: " ( )
AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15).
Data as JSON: /api/errors/e1dbeeea5d241e66.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/cores/storage.core.ts:300
const newStat = await this.storageRepository.stat(newPath);
const oldPathSize = assetInfo ? assetInfo.sizeInBytes : oldStat.size;
const newPathSize = newStat.size;
this.logger.debug(`File size check: ${newPathSize} === ${oldPathSize}`);
if (newPathSize !== oldPathSize) {
this.logger.warn(`Unable to complete move. File size mismatch: ${newPathSize} !== ${oldPathSize}`);
return false;
}
const repos = {
configRepo: this.configRepository,
metadataRepo: this.systemMetadataRepository,
logger: this.logger,
};
const config = await getConfig(repos, { withCache: true });
if (assetInfo && config.storageTemplate.hashVerificationEnabled) {
const { checksum } = assetInfo;
const newChecksum = await this.cryptoRepository.hashFile(newPath);
if (!newChecksum.equals(checksum)) {
this.logger.warn(
`Unable to complete move. File checksum mismatch: ${newChecksum.toString('base64')} !== ${checksum.toString(
'base64',
)}`,
);
return false;
}
this.logger.debug(`File checksum check: ${newChecksum.toString('base64')} === ${checksum.toString('base64')}`);
}
return true;
}
ensureFolders(input: string) {
this.storageRepository.mkdirSync(dirname(input));
}
removeEmptyDirs(folder: StorageFolder) {
return this.storageRepository.removeEmptyDirs(StorageCore.getBaseFolder(folder));
}View on GitHub (pinned to e55ac299a4)