Repository: microsoft/vscode (TypeScript) — Bug Fix

Issue:
Handle 405 error from server
Testing #161785

@rebornix and I figured out that the issue he is seeing an issue with data inconsistency on the server. Some how client has synced a profile with a collection id but that collection does not exist on the server. `profiles` resource has a profile with a collection id and this collection id does not exist on the server. 




Discussion:
@Tyriar Any idea how this can be possible on the server? From the client side, I am deleting the collection only after removing the profile with that collection id 

https://github.com/microsoft/vscode/blob/5c168e7c3907a95135a8d95436832094dc380358/src/vs/platform/userDataSync/common/userDataProfilesManifestSync.ts#L217-L222

I will also see if there is any possibility on the client side for this to happen.
Is the collection there if you query the manifest and `/v1/collection`? It's a little hard to know what the issue could be by just eye balling the code as I'm not very familiar with it.
> Is the collection there if you query the manifest

No collection does not exist in the manifest. 

> and /v1/collection

I do not know about it
There is a sync problem that I'm aware of, but it could happen if you connect to different regions, both of which have cached manifests. That will resolve itself over time.

> No collection does not exist in the manifest.

Does trying to recreate the collection result in an error?
> Does trying to recreate the collection result in an error?

No, I do not recreate the collection. I assume it exists if the profile resource contains a profile with collection url.
This should be fixed by https://github.com/microsoft/vscode/pull/165713

Resolving patch:
diff --git a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts
--- a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts
+++ b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts
@@ -20,7 +20,7 @@ import { IHeaders } from 'vs/base/parts/request/common/request';
 import { localize } from 'vs/nls';
 import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
 import { IEnvironmentService } from 'vs/platform/environment/common/environment';
-import { FileChangesEvent, FileOperationError, FileOperationResult, IFileContent, IFileService } from 'vs/platform/files/common/files';
+import { FileChangesEvent, FileOperationError, FileOperationResult, IFileContent, IFileService, toFileOperationResult } from 'vs/platform/files/common/files';
 import { ILogService } from 'vs/platform/log/common/log';
 import { getServiceMachineId } from 'vs/platform/externalServices/common/serviceMachineId';
 import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
@@ -540,8 +540,10 @@ export abstract class AbstractSynchroniser extends Disposable implements IUserDa
 		this.storageService.remove(this.lastSyncUserDataStateKey, StorageScope.APPLICATION);
 		try {
 			await this.fileService.del(this.lastSyncResource);
-		} catch (e) {
-			this.logService.error(e);
+		} catch (error) {
+			if (toFileOperationResult(error) !== FileOperationResult.FILE_NOT_FOUND) {
+				this.logService.error(error);
+			}
 		}
 	}
 
diff --git a/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts b/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts
--- a/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts
+++ b/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts
@@ -267,6 +267,12 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto
 			this.logService.info('Auto Sync: Turned off sync because of making too many requests to server');
 		}
 
+		// Method Not Found
+		else if (userDataSyncError.code === UserDataSyncErrorCode.MethodNotFound) {
+			await this.turnOff(false, true /* force soft turnoff on error */);
+			this.logService.info('Auto Sync: Turned off sync because current client is making requests to server that are not supported');
+		}
+
 		// Upgrade Required or Gone
 		else if (userDataSyncError.code === UserDataSyncErrorCode.UpgradeRequired || userDataSyncError.code === UserDataSyncErrorCode.Gone) {
 			await this.turnOff(false, true /* force soft turnoff on error */,
@@ -458,87 +464,103 @@ class AutoSync extends Disposable {
 	private async doSync(reason: string, disableCache: boolean, token: CancellationToken): Promise<void> {
 		this.logService.info(`Auto Sync: Triggered by ${reason}`);
 		this._onDidStartSync.fire();
+
 		let error: Error | undefined;
 		try {
-			this.syncTask = await this.userDataSyncService.createSyncTask(this.manifest, disableCache);
-			if (token.isCancellationRequested) {
-				return;
-			}
-			this.manifest = this.syncTask.manifest;
-
-			// Server has no data but this machine was synced before
-			if (this.manifest === null && await this.userDataSyncService.hasPreviouslySynced()) {
-				if (this.hasSyncServiceChanged()) {
-					if (await this.hasDefaultServiceChanged()) {
-						throw new UserDataAutoSyncError(localize('default service changed', "Cannot sync because default service has changed"), UserDataSyncErrorCode.DefaultServiceChanged);
-					} else {
-						throw new UserDataAutoSyncError(localize('service changed', "Cannot sync because sync service has changed"), UserDataSyncErrorCode.ServiceChanged);
-					}
-				} else {
-					// Sync was turned off in the cloud
-					throw new UserDataAutoSyncError(localize('turned off', "Cannot sync because syncing is turned off in the cloud"), UserDataSyncErrorCode.TurnedOff);
+			await this.createAndRunSyncTask(disableCache, token);
+		} catch (e) {
+			this.logService.error(e);
+			error = e;
+			if (UserDataSyncError.toUserDataSyncError(e).code === UserDataSyncErrorCode.MethodNotFound) {
+				try {
+					this.logService.info('Auto Sync: Client is making invalid requests. Cleaning up data...');
+					await this.userDataSyncService.cleanUpRemoteData();
+					this.logService.info('Auto Sync: Retrying sync...');
+					await this.createAndRunSyncTask(disableCache, token);
+					error = undefined;
+				} catch (e1) {
+					this.logService.error(e1);
+					error = e1;
 				}
 			}
+		}
 
-			const sessionId = this.storageService.get(sessionIdKey, StorageScope.APPLICATION);
-			// Server session is different from client session
-			if (sessionId && this.manifest && sessionId !== this.manifest.session) {
-				if (this.hasSyncServiceChanged()) {
-					if (await this.hasDefaultServiceChanged()) {
-						throw new UserDataAutoSyncError(localize('default service changed', "Cannot sync because default service has changed"), UserDataSyncErrorCode.DefaultServiceChanged);
-					} else {
-						throw new UserDataAutoSyncError(localize('service changed', "Cannot sync because sync service has changed"), UserDataSyncErrorCode.ServiceChanged);
-					}
+		this._onDidFinishSync.fire(error);
+	}
+
+	private async createAndRunSyncTask(disableCache: boolean, token: CancellationToken): Promise<void> {
+		this.syncTask = await this.userDataSyncService.createSyncTask(this.manifest, disableCache);
+		if (token.isCancellationRequested) {
+			return;
+		}
+		this.manifest = this.syncTask.manifest;
+
+		// Server has no data but this machine was synced before
+		if (this.manifest === null && await this.userDataSyncService.hasPreviouslySynced()) {
+			if (this.hasSyncServiceChanged()) {
+				if (await this.hasDefaultServiceChanged()) {
+					throw new UserDataAutoSyncError(localize('default service changed', "Cannot sync because default service has changed"), UserDataSyncErrorCode.DefaultServiceChanged);
 				} else {
-					throw new UserDataAutoSyncError(localize('session expired', "Cannot sync because current session is expired"), UserDataSyncErrorCode.SessionExpired);
+					throw new UserDataAutoSyncError(localize('service changed', "Cannot sync because sync service has changed"), UserDataSyncErrorCode.ServiceChanged);
 				}
+			} else {
+				// Sync was turned off in the cloud
+				throw new UserDataAutoSyncError(localize('turned off', "Cannot sync because syncing is turned off in the cloud"), UserDataSyncErrorCode.TurnedOff);
 			}
+		}
 
-			const machines = await this.userDataSyncMachinesService.getMachines(this.manifest || undefined);
-			// Return if cancellation is requested
-			if (token.isCancellationRequested) {
-				return;
-			}
-
-			const currentMachine = machines.find(machine => machine.isCurrent);
-			// Check if sync was turned off from other machine
-			if (currentMachine?.disabled) {
-				// Throw TurnedOff error
-				throw new UserDataAutoSyncError(localize('turned off machine', "Cannot sync because syncing is turned off on this machine from another machine."), UserDataSyncErrorCode.TurnedOff);
+		const sessionId = this.storageService.get(sessionIdKey, StorageScope.APPLICATION);
+		// Server session is different from client session
+		if (sessionId && this.manifest && sessionId !== this.manifest.session) {
+			if (this.hasSyncServiceChanged()) {
+				if (await this.hasDefaultServiceChanged()) {
+					throw new UserDataAutoSyncError(localize('default service changed', "Cannot sync because default service has changed"), UserDataSyncErrorCode.DefaultServiceChanged);
+				} else {
+					throw new UserDataAutoSyncError(localize('service changed', "Cannot sync because sync service has changed"), UserDataSyncErrorCode.ServiceChanged);
+				}
+			} else {
+				throw new UserDataAutoSyncError(localize('session expired', "Cannot sync because current session is expired"), UserDataSyncErrorCode.SessionExpired);
 			}
+		}
 
-			await this.syncTask.run();
+		const machines = await this.userDataSyncMachinesService.getMachines(this.manifest || undefined);
+		// Return if cancellation is requested
+		if (token.isCancellationRequested) {
+			return;
+		}
 
-			// After syncing, get the manifest if it was not available before
-			if (this.manifest === null) {
-				try {
-					this.manifest = await this.userDataSyncStoreService.manifest(null);
-				} catch (error) {
-					throw new UserDataAutoSyncError(toErrorMessage(error), error instanceof UserDataSyncError ? error.code : UserDataSyncErrorCode.Unknown);
-				}
-			}
+		const currentMachine = machines.find(machine => machine.isCurrent);
+		// Check if sync was turned off from other machine
+		if (currentMachine?.disabled) {
+			// Throw TurnedOff error
+			throw new UserDataAutoSyncError(localize('turned off machine', "Cannot sync because syncing is turned off on this machine from another machine."), UserDataSyncErrorCode.TurnedOff);
+		}
 
-			// Update local session id
-			if (this.manifest && this.manifest.session !== sessionId) {
-				this.storageService.store(sessionIdKey, this.manifest.session, StorageScope.APPLICATION, StorageTarget.MACHINE);
-			}
+		await this.syncTask.run();
 
-			// Return if cancellation is requested
-			if (token.isCancellationRequested) {
-				return;
+		// After syncing, get the manifest if it was not available before
+		if (this.manifest === null) {
+			try {
+				this.manifest = await this.userDataSyncStoreService.manifest(null);
+			} catch (error) {
+				throw new UserDataAutoSyncError(toErrorMessage(error), error instanceof UserDataSyncError ? error.code : UserDataSyncErrorCode.Unknown);
 			}
+		}
 
-			// Add current machine
-			if (!currentMachine) {
-				await this.userDataSyncMachinesService.addCurrentMachine(this.manifest || undefined);
-			}
+		// Update local session id
+		if (this.manifest && this.manifest.session !== sessionId) {
+			this.storageService.store(sessionIdKey, this.manifest.session, StorageScope.APPLICATION, StorageTarget.MACHINE);
+		}
 
-		} catch (e) {
-			this.logService.error(e);
-			error = e;
+		// Return if cancellation is requested
+		if (token.isCancellationRequested) {
+			return;
 		}
 
-		this._onDidFinishSync.fire(error);
+		// Add current machine
+		if (!currentMachine) {
+			await this.userDataSyncMachinesService.addCurrentMachine(this.manifest || undefined);
+		}
 	}
 
 	register<T extends IDisposable>(t: T): T {
diff --git a/src/vs/platform/userDataSync/common/userDataProfilesManifestMerge.ts b/src/vs/platform/userDataSync/common/userDataProfilesManifestMerge.ts
--- a/src/vs/platform/userDataSync/common/userDataProfilesManifestMerge.ts
+++ b/src/vs/platform/userDataSync/common/userDataProfilesManifestMerge.ts
@@ -20,26 +20,20 @@ interface IUserDataProfileInfo {
 }
 
 export function merge(local: IUserDataProfile[], remote: ISyncUserDataProfile[] | null, lastSync: ISyncUserDataProfile[] | null, ignored: string[]): IMergeResult {
-	const result: IRelaxedMergeResult = {
-		local: {
-			added: [],
-			removed: [],
-			updated: [],
-		}, remote: {
-			added: [],
-			removed: [],
-			updated: [],
-		}
-	};
+	const localResult: { added: ISyncUserDataProfile[]; removed: IUserDataProfile[]; updated: ISyncUserDataProfile[] } = { added: [], removed: [], updated: [] };
+	let remoteResult: { added: IUserDataProfile[]; removed: ISyncUserDataProfile[]; updated: IUserDataProfile[] } | null = { added: [], removed: [], updated: [] };
 
 	if (!remote) {
 		const added = local.filter(({ id }) => !ignored.includes(id));
 		if (added.length) {
-			result.remote!.added = added;
+			remoteResult.added = added;
 		} else {
-			result.remote = null;
+			remoteResult = null;
 		}
-		return result;
+		return {
+			local: localResult,
+			remote: remoteResult
+		};
 	}
 
 	const localToRemote = compare(local, remote, ignored);
@@ -52,7 +46,7 @@ export function merge(local: IUserDataProfile[], remote: ISyncUserDataProfile[]
 		for (const id of baseToRemote.removed) {
 			const e = local.find(profile => profile.id === id);
 			if (e) {
-				result.local.removed.push(e);
+				localResult.removed.push(e);
 			}
 		}
 
@@ -64,24 +58,24 @@ export function merge(local: IUserDataProfile[], remote: ISyncUserDataProfile[]
 				// Is different from local to remote
 				if (localToRemote.updated.includes(id)) {
 					// Remote wins always
-					result.local.updated.push(remoteProfile);
+					localResult.updated.push(remoteProfile);
 				}
 			} else {
-				result.local.added.push(remoteProfile);
+				localResult.added.push(remoteProfile);
 			}
 		}
 
 		// Remotely updated profiles
 		for (const id of baseToRemote.updated) {
 			// Remote wins always
-			result.local.updated.push(remote.find(profile => profile.id === id)!);
+			localResult.updated.push(remote.find(profile => profile.id === id)!);
 		}
 
 		// Locally added profiles
 		for (const id of baseToLocal.added) {
 			// Not there in remote
 			if (!baseToRemote.added.includes(id)) {
-				result.remote!.added.push(local.find(profile => profile.id === id)!);
+				remoteResult.added.push(local.find(profile => profile.id === id)!);
 			}
 		}
 
@@ -94,21 +88,24 @@ export function merge(local: IUserDataProfile[], remote: ISyncUserDataProfile[]
 
 			// If not updated in remote
 			if (!baseToRemote.updated.includes(id)) {
-				result.remote!.updated.push(local.find(profile => profile.id === id)!);
+				remoteResult.updated.push(local.find(profile => profile.id === id)!);
 			}
 		}
 
 		// Locally removed profiles
 		for (const id of baseToLocal.removed) {
-			result.remote!.removed.push(remote.find(profile => profile.id === id)!);
+			const removedProfile = remote.find(profile => profile.id === id);
+			if (removedProfile) {
+				remoteResult.removed.push(removedProfile);
+			}
 		}
 	}
 
-	if (result.remote!.added.length === 0 && result.remote!.removed.length === 0 && result.remote!.updated.length === 0) {
-		result.remote = null;
+	if (remoteResult.added.length === 0 && remoteResult.removed.length === 0 && remoteResult.updated.length === 0) {
+		remoteResult = null;
 	}
 
-	return result;
+	return { local: localResult, remote: remoteResult };
 }
 
 function compare(from: IUserDataProfileInfo[] | null, to: IUserDataProfileInfo[], ignoredProfiles: string[]): { added: string[]; removed: string[]; updated: string[] } {
diff --git a/src/vs/platform/userDataSync/common/userDataProfilesManifestSync.ts b/src/vs/platform/userDataSync/common/userDataProfilesManifestSync.ts
--- a/src/vs/platform/userDataSync/common/userDataProfilesManifestSync.ts
+++ b/src/vs/platform/userDataSync/common/userDataProfilesManifestSync.ts
@@ -179,6 +179,11 @@ export class UserDataProfilesManifestSynchroniser extends AbstractSynchroniser i
 			this.logService.info(`${this.syncResourceLogLabel}: No changes found during synchronizing profiles.`);
 		}
 
+		const remoteProfiles = resourcePreviews[0][0].remoteProfiles || [];
+		if (remoteProfiles.length + (remote?.added.length ?? 0) - (remote?.removed.length ?? 0) > 20) {
+			throw new UserDataSyncError('Too many profiles to sync. Please remove some profiles and try again.', UserDataSyncErrorCode.LocalTooManyProfiles);
+		}
+
 		if (localChange !== Change.None) {
 			await this.backupLocal(stringifyLocalProfiles(this.getLocalUserDataProfiles(), false));
 			const promises: Promise<any>[] = [];
@@ -212,11 +217,17 @@ export class UserDataProfilesManifestSynchroniser extends AbstractSynchroniser i
 		}
 
 		if (remoteChange !== Change.None) {
-			const remoteProfiles = resourcePreviews[0][0].remoteProfiles || [];
 			this.logService.trace(`${this.syncResourceLogLabel}: Updating remote profiles...`);
-			for (const profile of remote?.added || []) {
-				const collection = await this.userDataSyncStoreService.createCollection(this.syncHeaders);
-				remoteProfiles.push({ id: profile.id, name: profile.name, collection, shortName: profile.shortName });
+			const addedCollections: string[] = [];
+			const canAddRemoteProfiles = remoteProfiles.length + (remote?.added.length ?? 0) <= 20;
+			if (canAddRemoteProfiles) {
+				for (const profile of remote?.added || []) {
+					const collection = await this.userDataSyncStoreService.createCollection(this.syncHeaders);
+					addedCollections.push(collection);
+					remoteProfiles.push({ id: profile.id, name: profile.name, collection, shortName: profile.shortName });
+				}
+			} else {
+				this.logService.info(`${this.syncResourceLogLabel}: Could not create remote profiles as there are too many profiles.`);
 			}
 			for (const profile of remote?.removed || []) {
 				remoteProfiles.splice(remoteProfiles.findIndex(({ id }) => profile.id === id), 1);
@@ -227,8 +238,19 @@ export class UserDataProfilesManifestSynchroniser extends AbstractSynchroniser i
 					remoteProfiles.splice(remoteProfiles.indexOf(profileToBeUpdated), 1, { id: profile.id, name: profile.name, collection: profileToBeUpdated.collection, shortName: profile.shortName });
 				}
 			}
-			remoteUserData = await this.updateRemoteUserData(this.stringifyRemoteProfiles(remoteProfiles), force ? null : remoteUserData.ref);
-			this.logService.info(`${this.syncResourceLogLabel}: Updated remote profiles.${remote?.added.length ? ` Added: ${JSON.stringify(remote.added.map(e => e.name))}.` : ''}${remote?.updated.length ? ` Updated: ${JSON.stringify(remote.updated.map(e => e.name))}.` : ''}${remote?.removed.length ? ` Removed: ${JSON.stringify(remote.removed.map(e => e.name))}.` : ''}`);
+
+			try {
+				remoteUserData = await this.updateRemoteProfiles(remoteProfiles, force ? null : remoteUserData.ref);
+				this.logService.info(`${this.syncResourceLogLabel}: Updated remote profiles.${canAddRemoteProfiles && remote?.added.length ? ` Added: ${JSON.stringify(remote.added.map(e => e.name))}.` : ''}${remote?.updated.length ? ` Updated: ${JSON.stringify(remote.updated.map(e => e.name))}.` : ''}${remote?.removed.length ? ` Removed: ${JSON.stringify(remote.removed.map(e => e.name))}.` : ''}`);
+			} catch (error) {
+				if (addedCollections.length) {
+					this.logService.info(`${this.syncResourceLogLabel}: Failed to update remote profiles. Cleaning up added collections...`);
+					for (const collection of addedCollections) {
+						await this.userDataSyncStoreService.deleteCollection(collection, this.syncHeaders);
+					}
+				}
+				throw error;
+			}
 
 			for (const profile of remote?.removed || []) {
 				await this.userDataSyncStoreService.deleteCollection(profile.collection, this.syncHeaders);
@@ -243,6 +265,10 @@ export class UserDataProfilesManifestSynchroniser extends AbstractSynchroniser i
 		}
 	}
 
+	async updateRemoteProfiles(profiles: ISyncUserDataProfile[], ref: string | null): Promise<IRemoteUserData> {
+		return this.updateRemoteUserData(this.stringifyRemoteProfiles(profiles), ref);
+	}
+
 	async hasLocalData(): Promise<boolean> {
 		return this.getLocalUserDataProfiles().length > 0;
 	}
diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts
--- a/src/vs/platform/userDataSync/common/userDataSync.ts
+++ b/src/vs/platform/userDataSync/common/userDataSync.ts
@@ -238,6 +238,7 @@ export const enum UserDataSyncErrorCode {
 	// Client Errors (>= 400 )
 	Unauthorized = 'Unauthorized', /* 401 */
 	NotFound = 'NotFound', /* 404 */
+	MethodNotFound = 'MethodNotFound', /* 405 */
 	Conflict = 'Conflict', /* 409 */
 	Gone = 'Gone', /* 410 */
 	PreconditionFailed = 'PreconditionFailed', /* 412 */
@@ -261,6 +262,7 @@ export const enum UserDataSyncErrorCode {
 	SessionExpired = 'SessionExpired',
 	ServiceChanged = 'ServiceChanged',
 	DefaultServiceChanged = 'DefaultServiceChanged',
+	LocalTooManyProfiles = 'LocalTooManyProfiles',
 	LocalTooManyRequests = 'LocalTooManyRequests',
 	LocalPreconditionFailed = 'LocalPreconditionFailed',
 	LocalInvalidContent = 'LocalInvalidContent',
@@ -509,6 +511,7 @@ export interface IUserDataSyncService {
 
 	reset(): Promise<void>;
 	resetRemote(): Promise<void>;
+	cleanUpRemoteData(): Promise<void>;
 	resetLocal(): Promise<void>;
 	hasLocalData(): Promise<boolean>;
 	hasPreviouslySynced(): Promise<boolean>;
diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts
--- a/src/vs/platform/userDataSync/common/userDataSyncService.ts
+++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts
@@ -167,7 +167,23 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
 				return that.sync(manifest, true, executionId, cancellableToken.token);
 			},
 			async apply(): Promise<void> {
-				await that.applyManualSync(manifest, executionId, cancellableToken.token);
+				try {
+					try {
+						await that.applyManualSync(manifest, executionId, cancellableToken.token);
+					} catch (error) {
+						if (UserDataSyncError.toUserDataSyncError(error).code === UserDataSyncErrorCode.MethodNotFound) {
+							that.logService.info('Client is making invalid requests. Cleaning up data...');
+							await that.cleanUpRemoteData();
+							that.logService.info('Applying manual sync again...');
+							await that.applyManualSync(manifest, executionId, cancellableToken.token);
+						} else {
+							throw error;
+						}
+					}
+				} catch (error) {
+					that.logService.error(error);
+					throw error;
+				}
 				that.logService.info(`Sync done. Took ${new Date().getTime() - startTime}ms`);
 				that.updateLastSyncTime();
 			},
@@ -395,6 +411,29 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
 		this.logService.info('Did reset the local sync state.');
 	}
 
+	async cleanUpRemoteData(): Promise<void> {
+		const remoteProfiles = await this.userDataSyncResourceProviderService.getRemoteSyncedProfiles();
+		const remoteProfileCollections = remoteProfiles.map(profile => profile.collection);
+		const allCollections = await this.userDataSyncStoreService.getAllCollections();
+		const redundantCollections = allCollections.filter(c => !remoteProfileCollections.includes(c));
+		if (redundantCollections.length) {
+			this.logService.info(`Deleting ${redundantCollections.length} redundant collections on server`);
+			await Promise.allSettled(redundantCollections.map(collectionId => this.userDataSyncStoreService.deleteCollection(collectionId)));
+			this.logService.info(`Deleted redundant collections on server`);
+		}
+		const updatedRemoteProfiles = remoteProfiles.filter(profile => allCollections.includes(profile.collection));
+		if (updatedRemoteProfiles.length !== remoteProfiles.length) {
+			this.logService.info(`Updating remote profiles with invalid collections on server`);
+			const profileManifestSynchronizer = this.instantiationService.createInstance(UserDataProfilesManifestSynchroniser, this.userDataProfilesService.defaultProfile, undefined);
+			try {
+				await profileManifestSynchronizer.updateRemoteProfiles(updatedRemoteProfiles, null);
+				this.logService.info(`Updated remote profiles on server`);
+			} finally {
+				profileManifestSynchronizer.dispose();
+			}
+		}
+	}
+
 	private async performAction<T>(profile: IUserDataProfile, action: (synchroniser: IUserDataSynchroniser) => Promise<T | undefined>): Promise<T | null> {
 		const disposables = new DisposableStore();
 		try {
@@ -756,13 +795,13 @@ class ProfileSynchronizer extends Disposable {
 
 	private getOrder(syncResource: SyncResource): number {
 		switch (syncResource) {
-			case SyncResource.Profiles: return 0;
-			case SyncResource.Settings: return 1;
-			case SyncResource.Keybindings: return 2;
-			case SyncResource.Snippets: return 3;
-			case SyncResource.Tasks: return 4;
-			case SyncResource.GlobalState: return 5;
-			case SyncResource.Extensions: return 6;
+			case SyncResource.Settings: return 0;
+			case SyncResource.Keybindings: return 1;
+			case SyncResource.Snippets: return 2;
+			case SyncResource.Tasks: return 3;
+			case SyncResource.GlobalState: return 4;
+			case SyncResource.Extensions: return 5;
+			case SyncResource.Profiles: return 6;
 		}
 	}
 
@@ -771,10 +810,12 @@ class ProfileSynchronizer extends Disposable {
 function canBailout(e: any): boolean {
 	if (e instanceof UserDataSyncError) {
 		switch (e.code) {
+			case UserDataSyncErrorCode.MethodNotFound:
 			case UserDataSyncErrorCode.TooLarge:
 			case UserDataSyncErrorCode.TooManyRequests:
 			case UserDataSyncErrorCode.TooManyRequestsAndRetryAfter:
 			case UserDataSyncErrorCode.LocalTooManyRequests:
+			case UserDataSyncErrorCode.LocalTooManyProfiles:
 			case UserDataSyncErrorCode.Gone:
 			case UserDataSyncErrorCode.UpgradeRequired:
 			case UserDataSyncErrorCode.IncompatibleRemoteContent:
diff --git a/src/vs/platform/userDataSync/common/userDataSyncServiceIpc.ts b/src/vs/platform/userDataSync/common/userDataSyncServiceIpc.ts
--- a/src/vs/platform/userDataSync/common/userDataSyncServiceIpc.ts
+++ b/src/vs/platform/userDataSync/common/userDataSyncServiceIpc.ts
@@ -82,6 +82,7 @@ export class UserDataSyncChannel implements IServerChannel {
 			case 'replace': return this.service.replace(reviewSyncResourceHandle(args[0]));
 			case 'getAssociatedResources': return this.service.getAssociatedResources(reviewSyncResourceHandle(args[0]));
 			case 'getMachineId': return this.service.getMachineId(reviewSyncResourceHandle(args[0]));
+			case 'cleanUpRemoteData': return this.service.cleanUpRemoteData();
 
 			case 'createManualSyncTask': return this.createManualSyncTask();
 		}
@@ -95,7 +96,7 @@ export class UserDataSyncChannel implements IServerChannel {
 
 			switch (manualSyncTaskCommand) {
 				case 'merge': return manualSyncTask.merge();
-				case 'apply': return manualSyncTask.apply().finally(() => this.manualSyncTasks.delete(this.createKey(manualSyncTask.id)));
+				case 'apply': return manualSyncTask.apply().then(() => this.manualSyncTasks.delete(this.createKey(manualSyncTask.id)));
 				case 'stop': return manualSyncTask.stop().finally(() => this.manualSyncTasks.delete(this.createKey(manualSyncTask.id)));
 			}
 		}
@@ -246,6 +247,10 @@ export class UserDataSyncChannelClient extends Disposable implements IUserDataSy
 		return this.channel.call<string | undefined>('getMachineId', [syncResourceHandle]);
 	}
 
+	cleanUpRemoteData(): Promise<void> {
+		return this.channel.call('cleanUpRemoteData');
+	}
+
 	replace(syncResourceHandle: ISyncResourceHandle): Promise<void> {
 		return this.channel.call('replace', [syncResourceHandle]);
 	}
diff --git a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts
--- a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts
+++ b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts
@@ -20,7 +20,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
 import { IEnvironmentService } from 'vs/platform/environment/common/environment';
 import { IFileService } from 'vs/platform/files/common/files';
 import { IProductService } from 'vs/platform/product/common/productService';
-import { asJson, asTextOrError, IRequestService, isSuccess as isSuccessContext } from 'vs/platform/request/common/request';
+import { asJson, asText, asTextOrError, IRequestService, isSuccess as isSuccessContext } from 'vs/platform/request/common/request';
 import { getServiceMachineId } from 'vs/platform/externalServices/common/serviceMachineId';
 import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
 import { CONFIGURATION_SYNC_STORE_KEY, HEADER_EXECUTION_ID, HEADER_OPERATION_ID, IAuthenticationProvider, IResourceRefHandle, IUserData, IUserDataManifest, IUserDataSyncLogService, IUserDataSyncStore, IUserDataSyncStoreManagementService, IUserDataSyncStoreService, ServerResource, SYNC_SERVICE_URL_TYPE, UserDataSyncErrorCode, UserDataSyncStoreError, UserDataSyncStoreType } from 'vs/platform/userDataSync/common/userDataSync';
@@ -243,7 +243,7 @@ export class UserDataSyncStoreClient extends Disposable {
 
 		const context = await this.request(url, { type: 'GET', headers }, [], CancellationToken.None);
 
-		return (await asJson<string[]>(context)) || [];
+		return (await asJson<{ id: string }[]>(context))?.map(({ id }) => id) || [];
 	}
 
 	async createCollection(headers: IHeaders = {}): Promise<string> {
@@ -448,8 +448,8 @@ export class UserDataSyncStoreClient extends Disposable {
 			throw new Error('No settings sync store url configured.');
 		}
 
-		await this.deleteResources();
 		await this.deleteCollection();
+		await this.deleteResources();
 
 		// clear cached session.
 		this.clearSession();
@@ -529,10 +529,12 @@ export class UserDataSyncStoreClient extends Disposable {
 		const operationId = context.res.headers[HEADER_OPERATION_ID];
 		const requestInfo = { url, status: context.res.statusCode, 'execution-id': options.headers[HEADER_EXECUTION_ID], 'operation-id': operationId };
 		const isSuccess = isSuccessContext(context) || (context.res.statusCode && successCodes.indexOf(context.res.statusCode) !== -1);
+		let failureMessage = '';
 		if (isSuccess) {
 			this.logService.trace('Request succeeded', requestInfo);
 		} else {
 			this.logService.info('Request failed', requestInfo);
+			failureMessage = await asText(context) || '';
 		}
 
 		if (context.res.statusCode === 401) {
@@ -547,6 +549,10 @@ export class UserDataSyncStoreClient extends Disposable {
 			throw new UserDataSyncStoreError(`${options.type} request '${url}' failed because the requested resource is not found (404).`, url, UserDataSyncErrorCode.NotFound, context.res.statusCode, operationId);
 		}
 
+		if (context.res.statusCode === 405) {
+			throw new UserDataSyncStoreError(`${options.type} request '${url}' failed because the requested endpoint is not found (405). ${failureMessage}`, url, UserDataSyncErrorCode.MethodNotFound, context.res.statusCode, operationId);
+		}
+
 		if (context.res.statusCode === 409) {
 			throw new UserDataSyncStoreError(`${options.type} request '${url}' failed because of Conflict (409). There is new data for this resource. Make the request again with latest data.`, url, UserDataSyncErrorCode.Conflict, context.res.statusCode, operationId);
 		}
diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts
--- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts
+++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts
@@ -53,6 +53,7 @@ import { IHostService } from 'vs/workbench/services/host/browser/host';
 import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
 import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
 import { ctxIsMergeResultEditor, ctxMergeBaseUri } from 'vs/workbench/contrib/mergeEditor/common/mergeEditor';
+import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue';
 
 type ConfigureSyncQuickPickItem = { id: SyncResource; label: string; description?: string };
 
@@ -115,7 +116,9 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
 		@IAuthenticationService private readonly authenticationService: IAuthenticationService,
 		@IUserDataSyncStoreManagementService private readonly userDataSyncStoreManagementService: IUserDataSyncStoreManagementService,
 		@IConfigurationService private readonly configurationService: IConfigurationService,
-		@IHostService private readonly hostService: IHostService
+		@IHostService private readonly hostService: IHostService,
+		@ICommandService private readonly commandService: ICommandService,
+		@IWorkbenchIssueService private readonly workbenchIssueService: IWorkbenchIssueService
 	) {
 		super();
 
@@ -268,6 +271,10 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
 					this.handleTooLargeError(error.resource, localize('too large', "Disabled syncing {0} because size of the {1} file to sync is larger than {2}. Please open the file and reduce the size and enable sync", sourceArea.toLowerCase(), sourceArea.toLowerCase(), '100kb'), error);
 				}
 				break;
+			case UserDataSyncErrorCode.LocalTooManyProfiles:
+				this.disableSync(SyncResource.Profiles);
+				this.notificationService.error(localize('too many profiles', "Disabled syncing profiles because there are too many profiles to sync. Settings Sync supports syncing maximum 20 profiles. Please reduce the number of profiles and enable sync"));
+				break;
 			case UserDataSyncErrorCode.IncompatibleLocalContent:
 			case UserDataSyncErrorCode.Gone:
 			case UserDataSyncErrorCode.UpgradeRequired: {
@@ -279,6 +286,21 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
 				});
 				break;
 			}
+			case UserDataSyncErrorCode.MethodNotFound: {
+				const message = localize('method not found', "Settings sync is disabled because the client is making invalid requests. Please report an issue with the logs.");
+				const operationId = error.operationId ? localize('operationId', "Operation Id: {0}", error.operationId) : undefined;
+				this.notificationService.notify({
+					severity: Severity.Error,
+					message: operationId ? `${message} ${operationId}` : message,
+					actions: {
+						primary: [
+							new Action('Show Sync Logs', localize('show sync logs', "Show Log"), undefined, true, () => this.commandService.executeCommand(SHOW_SYNC_LOG_COMMAND_ID)),
+							new Action('Report Issue', localize('report issue', "Report Issue"), undefined, true, () => this.workbenchIssueService.openReporter())
+						]
+					}
+				});
+				break;
+			}
 			case UserDataSyncErrorCode.IncompatibleRemoteContent:
 				this.notificationService.notify({
 					severity: Severity.Error,
@@ -619,6 +641,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
 			case SyncResource.Tasks: return this.userDataSyncEnablementService.setResourceEnablement(SyncResource.Tasks, false);
 			case SyncResource.Extensions: return this.userDataSyncEnablementService.setResourceEnablement(SyncResource.Extensions, false);
 			case SyncResource.GlobalState: return this.userDataSyncEnablementService.setResourceEnablement(SyncResource.GlobalState, false);
+			case SyncResource.Profiles: return this.userDataSyncEnablementService.setResourceEnablement(SyncResource.Profiles, false);
 		}
 	}
 


Test patch:
diff --git a/src/vs/platform/userDataSync/test/common/userDataAutoSyncService.test.ts b/src/vs/platform/userDataSync/test/common/userDataAutoSyncService.test.ts
--- a/src/vs/platform/userDataSync/test/common/userDataAutoSyncService.test.ts
+++ b/src/vs/platform/userDataSync/test/common/userDataAutoSyncService.test.ts
@@ -149,8 +149,6 @@ suite('UserDataAutoSyncService', () => {
 				{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
 				// Machines
 				{ type: 'GET', url: `${target.url}/v1/resource/machines/latest`, headers: {} },
-				// Profiles
-				{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} },
 				// Settings
 				{ type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} },
 				{ type: 'POST', url: `${target.url}/v1/resource/settings`, headers: { 'If-Match': '0' } },
@@ -168,6 +166,8 @@ suite('UserDataAutoSyncService', () => {
 				{ type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '0' } },
 				// Extensions
 				{ type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} },
+				// Profiles
+				{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} },
 				// Manifest
 				{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
 				// Machines
diff --git a/src/vs/platform/userDataSync/test/common/userDataProfilesManifestMerge.test.ts b/src/vs/platform/userDataSync/test/common/userDataProfilesManifestMerge.test.ts
--- a/src/vs/platform/userDataSync/test/common/userDataProfilesManifestMerge.test.ts
+++ b/src/vs/platform/userDataSync/test/common/userDataProfilesManifestMerge.test.ts
@@ -173,4 +173,24 @@ suite('UserDataProfilesManifestMerge', () => {
 		assert.strictEqual(actual.remote, null);
 	});
 
+	test('merge when profile is removed locally, but not exists in remote', () => {
+		const localProfiles: IUserDataProfile[] = [
+			toUserDataProfile('1', '1', URI.file('1')),
+		];
+		const base: ISyncUserDataProfile[] = [
+			{ id: '1', name: '1', collection: '1' },
+			{ id: '2', name: '2', collection: '2' },
+		];
+		const remoteProfiles: ISyncUserDataProfile[] = [
+			{ id: '1', name: '3', collection: '1' },
+		];
+
+		const actual = merge(localProfiles, remoteProfiles, base, []);
+
+		assert.deepStrictEqual(actual.local.added, []);
+		assert.deepStrictEqual(actual.local.removed, []);
+		assert.deepStrictEqual(actual.local.updated, remoteProfiles);
+		assert.strictEqual(actual.remote, null);
+	});
+
 });
diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncService.test.ts b/src/vs/platform/userDataSync/test/common/userDataSyncService.test.ts
--- a/src/vs/platform/userDataSync/test/common/userDataSyncService.test.ts
+++ b/src/vs/platform/userDataSync/test/common/userDataSyncService.test.ts
@@ -32,8 +32,6 @@ suite('UserDataSyncService', () => {
 		assert.deepStrictEqual(target.requests, [
 			// Manifest
 			{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
-			// Profiles
-			{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} },
 			// Settings
 			{ type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} },
 			{ type: 'POST', url: `${target.url}/v1/resource/settings`, headers: { 'If-Match': '0' } },
@@ -51,6 +49,8 @@ suite('UserDataSyncService', () => {
 			{ type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '0' } },
 			// Extensions
 			{ type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} },
+			// Profiles
+			{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} },
 		]);
 
 	});
@@ -69,8 +69,6 @@ suite('UserDataSyncService', () => {
 		assert.deepStrictEqual(target.requests, [
 			// Manifest
 			{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
-			// Profiles
-			{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} },
 			// Keybindings
 			{ type: 'GET', url: `${target.url}/v1/resource/keybindings/latest`, headers: {} },
 			{ type: 'POST', url: `${target.url}/v1/resource/keybindings`, headers: { 'If-Match': '0' } },
@@ -85,6 +83,8 @@ suite('UserDataSyncService', () => {
 			{ type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '0' } },
 			// Extensions
 			{ type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} },
+			// Profiles
+			{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} },
 		]);
 
 	});
@@ -102,8 +102,6 @@ suite('UserDataSyncService', () => {
 		assert.deepStrictEqual(target.requests, [
 			// Manifest
 			{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
-			// Profiles
-			{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} },
 			// Settings
 			{ type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} },
 			// Keybindings
@@ -116,6 +114,8 @@ suite('UserDataSyncService', () => {
 			{ type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: {} },
 			// Extensions
 			{ type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} },
+			// Profiles
+			{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} },
 		]);
 
 	});
@@ -139,13 +139,13 @@ suite('UserDataSyncService', () => {
 
 		assert.deepStrictEqual(target.requests, [
 			{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
-			{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} },
 			{ type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} },
 			{ type: 'GET', url: `${target.url}/v1/resource/keybindings/latest`, headers: {} },
 			{ type: 'GET', url: `${target.url}/v1/resource/snippets/latest`, headers: {} },
 			{ type: 'GET', url: `${target.url}/v1/resource/tasks/latest`, headers: {} },
 			{ type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: {} },
 			{ type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} },
+			{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} },
 		]);
 
 	});
@@ -177,7 +177,6 @@ suite('UserDataSyncService', () => {
 
 		assert.deepStrictEqual(target.requests, [
 			{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
-			{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} },
 			{ type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} },
 			{ type: 'POST', url: `${target.url}/v1/resource/settings`, headers: { 'If-Match': '1' } },
 			{ type: 'GET', url: `${target.url}/v1/resource/keybindings/latest`, headers: {} },
@@ -187,6 +186,7 @@ suite('UserDataSyncService', () => {
 			{ type: 'GET', url: `${target.url}/v1/resource/tasks/latest`, headers: {} },
 			{ type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: {} },
 			{ type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} },
+			{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} },
 		]);
 
 	});
@@ -219,9 +219,6 @@ suite('UserDataSyncService', () => {
 
 		assert.deepStrictEqual(target.requests, [
 			{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
-			{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} },
-			{ type: 'POST', url: `${target.url}/v1/collection`, headers: {} },
-			{ type: 'POST', url: `${target.url}/v1/resource/profiles`, headers: { 'If-Match': '0' } },
 			{ type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} },
 			{ type: 'POST', url: `${target.url}/v1/resource/settings`, headers: { 'If-Match': '1' } },
 			{ type: 'GET', url: `${target.url}/v1/resource/keybindings/latest`, headers: {} },
@@ -231,6 +228,9 @@ suite('UserDataSyncService', () => {
 			{ type: 'GET', url: `${target.url}/v1/resource/tasks/latest`, headers: {} },
 			{ type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: {} },
 			{ type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} },
+			{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} },
+			{ type: 'POST', url: `${target.url}/v1/collection`, headers: {} },
+			{ type: 'POST', url: `${target.url}/v1/resource/profiles`, headers: { 'If-Match': '0' } },
 			{ type: 'GET', url: `${target.url}/v1/collection/1/resource/settings/latest`, headers: {} },
 			{ type: 'GET', url: `${target.url}/v1/collection/1/resource/keybindings/latest`, headers: {} },
 			{ type: 'GET', url: `${target.url}/v1/collection/1/resource/snippets/latest`, headers: {} },
@@ -322,9 +322,6 @@ suite('UserDataSyncService', () => {
 		assert.deepStrictEqual(target.requests, [
 			// Manifest
 			{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
-			// Profiles
-			{ type: 'POST', url: `${target.url}/v1/collection`, headers: {} },
-			{ type: 'POST', url: `${target.url}/v1/resource/profiles`, headers: { 'If-Match': '0' } },
 			// Settings
 			{ type: 'POST', url: `${target.url}/v1/resource/settings`, headers: { 'If-Match': '1' } },
 			// Keybindings
@@ -333,6 +330,9 @@ suite('UserDataSyncService', () => {
 			{ type: 'POST', url: `${target.url}/v1/resource/snippets`, headers: { 'If-Match': '1' } },
 			// Global state
 			{ type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '1' } },
+			// Profiles
+			{ type: 'POST', url: `${target.url}/v1/collection`, headers: {} },
+			{ type: 'POST', url: `${target.url}/v1/resource/profiles`, headers: { 'If-Match': '0' } },
 			{ type: 'GET', url: `${target.url}/v1/collection/1/resource/settings/latest`, headers: {} },
 			{ type: 'GET', url: `${target.url}/v1/collection/1/resource/keybindings/latest`, headers: {} },
 			{ type: 'GET', url: `${target.url}/v1/collection/1/resource/snippets/latest`, headers: {} },
@@ -452,8 +452,6 @@ suite('UserDataSyncService', () => {
 		assert.deepStrictEqual(target.requests, [
 			// Manifest
 			{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
-			// Profiles
-			{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: { 'If-None-Match': '0' } },
 			// Settings
 			{ type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: { 'If-None-Match': '1' } },
 			// Keybindings
@@ -462,6 +460,8 @@ suite('UserDataSyncService', () => {
 			{ type: 'GET', url: `${target.url}/v1/resource/snippets/latest`, headers: { 'If-None-Match': '1' } },
 			// Global state
 			{ type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: { 'If-None-Match': '1' } },
+			// Profiles
+			{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: { 'If-None-Match': '0' } },
 			{ type: 'GET', url: `${target.url}/v1/collection/1/resource/settings/latest`, headers: {} },
 			{ type: 'GET', url: `${target.url}/v1/collection/1/resource/keybindings/latest`, headers: {} },
 			{ type: 'GET', url: `${target.url}/v1/collection/1/resource/snippets/latest`, headers: {} },
@@ -487,8 +487,8 @@ suite('UserDataSyncService', () => {
 
 		assert.deepStrictEqual(target.requests, [
 			// Manifest
-			{ type: 'DELETE', url: `${target.url}/v1/resource`, headers: {} },
 			{ type: 'DELETE', url: `${target.url}/v1/collection`, headers: {} },
+			{ type: 'DELETE', url: `${target.url}/v1/resource`, headers: {} },
 		]);
 
 	});
@@ -512,8 +512,6 @@ suite('UserDataSyncService', () => {
 		assert.deepStrictEqual(target.requests, [
 			// Manifest
 			{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
-			// Profiles
-			{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} },
 			// Settings
 			{ type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} },
 			{ type: 'POST', url: `${target.url}/v1/resource/settings`, headers: { 'If-Match': '0' } },
@@ -531,6 +529,8 @@ suite('UserDataSyncService', () => {
 			{ type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '0' } },
 			// Extensions
 			{ type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} },
+			// Profiles
+			{ type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} },
 		]);
 
 	});