diff --git a/src/app/imex/sync/sync-wrapper.service.spec.ts b/src/app/imex/sync/sync-wrapper.service.spec.ts index 6309b4c41f..9d95dc25dc 100644 --- a/src/app/imex/sync/sync-wrapper.service.spec.ts +++ b/src/app/imex/sync/sync-wrapper.service.spec.ts @@ -392,6 +392,41 @@ describe('SyncWrapperService', () => { expect(mockProviderManager.setSyncStatus).not.toHaveBeenCalled(); }); + + it('should set ERROR and return HANDLED_ERROR when upload has rejected ops', async () => { + mockSyncService.uploadPendingOps.and.returnValue( + Promise.resolve({ + uploadedCount: 0, + rejectedCount: 1, + piggybackedOps: [], + rejectedOps: [{ opId: 'test-op', error: 'Payload too complex (max depth 50)' }], + localWinOpsCreated: 0, + }), + ); + + const result = await service.sync(); + + expect(result).toBe('HANDLED_ERROR'); + expect(mockProviderManager.setSyncStatus).toHaveBeenCalledWith('ERROR'); + expect(mockProviderManager.setSyncStatus).not.toHaveBeenCalledWith('IN_SYNC'); + }); + + it('should set ERROR for non-payload rejected ops', async () => { + mockSyncService.uploadPendingOps.and.returnValue( + Promise.resolve({ + uploadedCount: 0, + rejectedCount: 1, + piggybackedOps: [], + rejectedOps: [{ opId: 'test-op', error: 'Some other rejection' }], + localWinOpsCreated: 0, + }), + ); + + const result = await service.sync(); + + expect(result).toBe('HANDLED_ERROR'); + expect(mockProviderManager.setSyncStatus).toHaveBeenCalledWith('ERROR'); + }); }); describe('Error handling', () => { diff --git a/src/app/imex/sync/sync-wrapper.service.ts b/src/app/imex/sync/sync-wrapper.service.ts index 3ed72d8c35..d1a0e71041 100644 --- a/src/app/imex/sync/sync-wrapper.service.ts +++ b/src/app/imex/sync/sync-wrapper.service.ts @@ -238,11 +238,9 @@ export class SyncWrapperService { 'SyncWrapperService: Upload rejected - payload too large/complex', uploadResult.rejectedOps, ); - this._providerManager.setSyncStatus('SYNC_ERROR'); - this._snackService.open({ - msg: T.F.SYNC.S.ERROR_PAYLOAD_TOO_LARGE, - type: 'ERROR', - }); + this._providerManager.setSyncStatus('ERROR'); + // Use alert for maximum visibility - this is a critical error + alert(this._translateService.instant(T.F.SYNC.S.ERROR_PAYLOAD_TOO_LARGE)); return 'HANDLED_ERROR'; } @@ -251,7 +249,7 @@ export class SyncWrapperService { 'SyncWrapperService: Upload had rejected operations, not marking as IN_SYNC', uploadResult.rejectedOps, ); - this._providerManager.setSyncStatus('SYNC_ERROR'); + this._providerManager.setSyncStatus('ERROR'); return 'HANDLED_ERROR'; } diff --git a/src/app/t.const.ts b/src/app/t.const.ts index 977c137af8..ab066e9c91 100644 --- a/src/app/t.const.ts +++ b/src/app/t.const.ts @@ -1099,6 +1099,7 @@ const T = { REMOTE_DATA_TOO_OLD: 'F.SYNC.S.REMOTE_DATA_TOO_OLD', STORAGE_QUOTA_EXCEEDED: 'F.SYNC.S.STORAGE_QUOTA_EXCEEDED', STORAGE_RECOVERED_AFTER_COMPACTION: 'F.SYNC.S.STORAGE_RECOVERED_AFTER_COMPACTION', + ERROR_PAYLOAD_TOO_LARGE: 'F.SYNC.S.ERROR_PAYLOAD_TOO_LARGE', SUCCESS_DOWNLOAD: 'F.SYNC.S.SUCCESS_DOWNLOAD', SUCCESS_VIA_BUTTON: 'F.SYNC.S.SUCCESS_VIA_BUTTON', UNKNOWN_ERROR: 'F.SYNC.S.UNKNOWN_ERROR', diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 5165dd4ef0..07d25142ef 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -1076,6 +1076,7 @@ "REMOTE_DATA_TOO_OLD": "Remote data is too old to be processed. Please update your remote app.", "STORAGE_QUOTA_EXCEEDED": "Sync server storage is full. Your changes are NOT syncing. Please archive old tasks or upgrade your plan.", "STORAGE_RECOVERED_AFTER_COMPACTION": "Storage was running low. Old data cleaned up successfully.", + "ERROR_PAYLOAD_TOO_LARGE": "Sync Error: Your data is too large to upload.\n\nThis happens when you have a lot of archived tasks. Your data was NOT synced.\n\nPlease contact support for assistance.", "SUCCESS_DOWNLOAD": "Synced data from remote", "SUCCESS_VIA_BUTTON": "Data successfully synced", "UNKNOWN_ERROR": "Unknown Sync Error: {{err}}",