{"record":{"id":"2a3606ac0b0d4af1","repo":"immich-app/immich","slug":"unable-to-complete-move-file-size-mismatch-newpathsize","errorCode":null,"errorMessage":"Unable to complete move. File size mismatch: ${newPathSize} !== ${oldPathSize}","messagePattern":"Unable to complete move\\. File size mismatch: (.+?) !== (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"server/src/cores/storage.core.ts","lineNumber":287,"sourceCode":"      }\n    }\n\n    await this.savePath(pathType, entityId, newPath, ownerId);\n    await this.moveRepository.delete(move.id);\n  }\n\n  private async verifyNewPathContentsMatchesExpected(\n    oldPath: string,\n    newPath: string,\n    assetInfo?: { sizeInBytes: number; checksum: Buffer },\n  ) {\n    const oldStat = await this.storageRepository.stat(oldPath);\n    const newStat = await this.storageRepository.stat(newPath);\n    const oldPathSize = assetInfo ? assetInfo.sizeInBytes : oldStat.size;\n    const newPathSize = newStat.size;\n    this.logger.debug(`File size check: ${newPathSize} === ${oldPathSize}`);\n    if (newPathSize !== oldPathSize) {\n      this.logger.warn(`Unable to complete move. File size mismatch: ${newPathSize} !== ${oldPathSize}`);\n      return false;\n    }\n    const repos = {\n      configRepo: this.configRepository,\n      metadataRepo: this.systemMetadataRepository,\n      logger: this.logger,\n    };\n    const config = await getConfig(repos, { withCache: true });\n    if (assetInfo && config.storageTemplate.hashVerificationEnabled) {\n      const { checksum } = assetInfo;\n      const newChecksum = await this.cryptoRepository.hashFile(newPath);\n      if (!newChecksum.equals(checksum)) {\n        this.logger.warn(\n          `Unable to complete move. File checksum mismatch: ${newChecksum.toString('base64')} !== ${checksum.toString(\n            'base64',\n          )}`,\n        );\n        return false;","sourceCodeStart":269,"sourceCodeEnd":305,"githubUrl":"https://github.com/immich-app/immich/blob/e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c/server/src/cores/storage.core.ts#L269-L305","documentation":"verifyNewPathContentsMatchesExpected compares the new file's size against the old file's size (or the asset's recorded sizeInBytes). On mismatch it logs this warning and returns false, causing callers to treat the move as failed and roll back the copied file.","triggerScenarios":"moveFile's copy fallback calls this verifier; stat(newPath).size differs from oldStat.size or assetInfo.sizeInBytes — copy did not fully complete or the source changed during copy.","commonSituations":"Disk filled up mid-copy; source asset being uploaded/synced concurrently; filesystem reporting sparse/incorrect sizes; copy interrupted by unmount.","solutions":["Re-run the move once source and destination are stable","Check destination free space","Freeze writes to the asset (stop sync/upload jobs) during migration","Compare checksums manually to see if only size metadata differs"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"const [oldStat, newStat] = await Promise.all([fs.stat(oldPath), fs.stat(newPath)]);\nif (oldStat.size !== newStat.size) throw new Error('size mismatch, retry copy');","typeGuard":null,"tryCatchPattern":"if (!(await verifyNewPathContentsMatchesExpected(oldPath, newPath, assetInfo))) {\n  await unlink(newPath);\n  await retryCopy(oldPath, newPath, { attempts: 3, backoff: 'exponential' });\n}","preventionTips":["Check free space before copy","Do not mutate source files while a move is in flight","Log old/new sizes on mismatch for diagnosis","Retry copies once rather than leaving files half-migrated"],"tags":["filesystem","file-move","verification","size-mismatch"],"backgroundTag":"checksum-mismatch","analyzedSha":"e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c","analyzedAt":"2026-09-15T07:20:19.675Z","contentChangedAt":"2026-09-15T07:20:19.675Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}