diff --git a/chrome/content/zotero/xpcom/sync/syncEngine.js b/chrome/content/zotero/xpcom/sync/syncEngine.js
index 08280ce0e..25d552ff7 100644
--- a/chrome/content/zotero/xpcom/sync/syncEngine.js
+++ b/chrome/content/zotero/xpcom/sync/syncEngine.js
@@ -1137,6 +1137,7 @@ Zotero.Sync.Data.Engine.prototype._uploadObjects = Zotero.Promise.coroutine(func
 			// Handle failed objects
 			for (let index in results.failed) {
 				let { code, message, data } = results.failed[index];
+				let key = jsonBatch[index].key;
 				// API errors are HTML
 				message = Zotero.Utilities.unescapeHTML(message);
 				let e = new Error(message);
@@ -1145,12 +1146,13 @@ Zotero.Sync.Data.Engine.prototype._uploadObjects = Zotero.Promise.coroutine(func
 				if (data) {
 					e.data = data;
 				}
-				Zotero.logError(`Error ${code} for ${objectType} ${jsonBatch[index].key} in `
+				e.objectType = objectType;
+				e.object = objectsClass.getByLibraryAndKey(this.libraryID, key);
+				
+				Zotero.logError(`Error ${code} for ${objectType} ${key} in `
 					+ this.library.name + ":\n\n" + e);
 				
-				let keepGoing = yield this._checkObjectUploadError(
-					objectType, jsonBatch[index].key, e, queue
-				);
+				let keepGoing = yield this._checkObjectUploadError(objectType, key, e, queue, batch);
 				if (keepGoing) {
 					numSuccessful++;
 					continue;
@@ -1604,7 +1606,7 @@ Zotero.Sync.Data.Engine.prototype._handleUploadError = Zotero.Promise.coroutine(
 });
 
 
-Zotero.Sync.Data.Engine.prototype._checkObjectUploadError = Zotero.Promise.coroutine(function* (objectType, key, e) {
+Zotero.Sync.Data.Engine.prototype._checkObjectUploadError = Zotero.Promise.coroutine(function* (objectType, key, e, queue, batch) {
 	var { code, data, message } = e;
 	
 	// If an item's dependency is missing remotely and it isn't in the queue (which
@@ -1663,35 +1665,7 @@ Zotero.Sync.Data.Engine.prototype._checkObjectUploadError = Zotero.Promise.corou
 	else if (code == 404 || code == 412) {
 		throw e;
 	}
-	else if (code == 413) {
-		// Note too long
-		if (objectType == 'item') {
-			let item = Zotero.Items.getByLibraryAndKey(this.libraryID, key);
-			if (item) {
-				// Throw an error that adds a button for selecting the item to the sync error dialog
-				if (message.includes('<img src="data:image')) {
-					// TODO: Localize
-					message = "Notes with embedded images cannot currently be synced to "
-						+ `${ZOTERO_CONFIG.DOMAIN_NAME}.`
-				}
-				
-				e = new Zotero.Error(
-					message,
-					0,
-					{
-						dialogButtonText: Zotero.getString('pane.items.showItemInLibrary'),
-						dialogButtonCallback: () => {
-							var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
-								.getService(Components.interfaces.nsIWindowMediator);
-							var win = wm.getMostRecentWindow("navigator:browser");
-							win.ZoteroPane.selectItem(item.id);
-						}
-					}
-				);
-			}
-			throw e;
-		}
-	}
+	
 	return false;
 });
 
diff --git a/chrome/content/zotero/xpcom/sync/syncRunner.js b/chrome/content/zotero/xpcom/sync/syncRunner.js
index e3f001aa8..8ad35e803 100644
--- a/chrome/content/zotero/xpcom/sync/syncRunner.js
+++ b/chrome/content/zotero/xpcom/sync/syncRunner.js
@@ -981,79 +981,104 @@ Zotero.Sync.Runner_Module = function (options = {}) {
 			}
 		}
 		else if (e.name && e.name == 'ZoteroObjectUploadError') {
+			let { code, data, objectType, object } = e;
+			
 			// Tag too long
-			if (e.code == 413 && e.data && e.data.tag !== undefined) {
-				// Show long tag fixer and handle result
-				e.dialogButtonText = Zotero.getString('general.fix');
-				e.dialogButtonCallback = Zotero.Promise.coroutine(function* () {
-					var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
-					   .getService(Components.interfaces.nsIWindowMediator);
-					var lastWin = wm.getMostRecentWindow("navigator:browser");
-					
-					// Open long tag fixer for every long tag in every editable library we're syncing
-					var editableLibraries = options.libraries
-						.filter(x => Zotero.Libraries.get(x).editable);
-					for (let libraryID of editableLibraries) {
-						let oldTagIDs = yield Zotero.Tags.getLongTagsInLibrary(libraryID);
-						for (let oldTagID of oldTagIDs) {
-							let oldTag = Zotero.Tags.getName(oldTagID);
-							let dataOut = { result: null };
-							lastWin.openDialog(
-								'chrome://zotero/content/longTagFixer.xul',
-								'',
-								'chrome,modal,centerscreen',
-								oldTag,
-								dataOut
-							);
-							// If dialog was cancelled, stop
-							if (!dataOut.result) {
-								return;
-							}
-							switch (dataOut.result.op) {
-							case 'split':
-								for (let libraryID of editableLibraries) {
-									let itemIDs = yield Zotero.Tags.getTagItems(libraryID, oldTagID);
-									yield Zotero.DB.executeTransaction(function* () {
-										for (let itemID of itemIDs) {
-											let item = yield Zotero.Items.getAsync(itemID);
-											for (let tag of dataOut.result.tags) {
-												item.addTag(tag);
+			if (code == 413 && objectType == 'item') {
+				if (data && data.tag !== undefined) {
+					// Show long tag fixer and handle result
+					e.dialogButtonText = Zotero.getString('general.fix');
+					e.dialogButtonCallback = Zotero.Promise.coroutine(function* () {
+						var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
+						   .getService(Components.interfaces.nsIWindowMediator);
+						var lastWin = wm.getMostRecentWindow("navigator:browser");
+						
+						// Open long tag fixer for every long tag in every editable library we're syncing
+						var editableLibraries = options.libraries
+							.filter(x => Zotero.Libraries.get(x).editable);
+						for (let libraryID of editableLibraries) {
+							let oldTagIDs = yield Zotero.Tags.getLongTagsInLibrary(libraryID);
+							for (let oldTagID of oldTagIDs) {
+								let oldTag = Zotero.Tags.getName(oldTagID);
+								let dataOut = { result: null };
+								lastWin.openDialog(
+									'chrome://zotero/content/longTagFixer.xul',
+									'',
+									'chrome,modal,centerscreen',
+									oldTag,
+									dataOut
+								);
+								// If dialog was cancelled, stop
+								if (!dataOut.result) {
+									return;
+								}
+								switch (dataOut.result.op) {
+								case 'split':
+									for (let libraryID of editableLibraries) {
+										let itemIDs = yield Zotero.Tags.getTagItems(libraryID, oldTagID);
+										yield Zotero.DB.executeTransaction(function* () {
+											for (let itemID of itemIDs) {
+												let item = yield Zotero.Items.getAsync(itemID);
+												for (let tag of dataOut.result.tags) {
+													item.addTag(tag);
+												}
+												item.removeTag(oldTag);
+												yield item.save();
 											}
-											item.removeTag(oldTag);
-											yield item.save();
-										}
-										yield Zotero.Tags.purge(oldTagID);
-									});
+											yield Zotero.Tags.purge(oldTagID);
+										});
+									}
+									break;
+								
+								case 'edit':
+									for (let libraryID of editableLibraries) {
+										let itemIDs = yield Zotero.Tags.getTagItems(libraryID, oldTagID);
+										yield Zotero.DB.executeTransaction(function* () {
+											for (let itemID of itemIDs) {
+												let item = yield Zotero.Items.getAsync(itemID);
+												item.replaceTag(oldTag, dataOut.result.tag);
+												yield item.save();
+											}
+										});
+									}
+									break;
+								
+								case 'delete':
+									for (let libraryID of editableLibraries) {
+										yield Zotero.Tags.removeFromLibrary(libraryID, oldTagID);
+									}
+									break;
 								}
-								break;
-							
-							case 'edit':
-								for (let libraryID of editableLibraries) {
-									let itemIDs = yield Zotero.Tags.getTagItems(libraryID, oldTagID);
-									yield Zotero.DB.executeTransaction(function* () {
-										for (let itemID of itemIDs) {
-											let item = yield Zotero.Items.getAsync(itemID);
-											item.replaceTag(oldTag, dataOut.result.tag);
-											yield item.save();
-										}
-									});
-								}
-								break;
-							
-							case 'delete':
-								for (let libraryID of editableLibraries) {
-									yield Zotero.Tags.removeFromLibrary(libraryID, oldTagID);
-								}
-								break;
 							}
 						}
+						
+						options.restartSync = true;
+					});
+				}
+				// Note too long
+				else if (object.isNote()) {
+					// Throw an error that adds a button for selecting the item to the sync error dialog
+					if (e.message.includes('<img src="data:image')) {
+						// TODO: Localize
+						e.message = "Notes with embedded images cannot currently be synced to "
+							+ `${ZOTERO_CONFIG.DOMAIN_NAME}.`
 					}
 					
-					options.restartSync = true;
-				});
-				// If not a background sync, show fixer dialog immediately
-				if (!options.background) {
-					yield e.dialogButtonCallback();
+					e.dialogButtonText = Zotero.getString('pane.items.showItemInLibrary');
+					e.dialogButtonCallback = () => {
+						var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
+							.getService(Components.interfaces.nsIWindowMediator);
+						var win = wm.getMostRecentWindow("navigator:browser");
+						win.ZoteroPane.selectItem(object.id);
+					};
+				}
+				
+				// If not a background sync, show dialog immediately
+				if (!options.background && e.dialogButtonCallback) {
+					let maybePromise = e.dialogButtonCallback();
+					if (maybePromise && maybePromise.then) {
+						yield maybePromise;
+					}
 				}
 			}
 		}
diff --git a/test/tests/syncRunnerTest.js b/test/tests/syncRunnerTest.js
index b161cac43..4d4b002e5 100644
--- a/test/tests/syncRunnerTest.js
+++ b/test/tests/syncRunnerTest.js
@@ -1033,6 +1033,53 @@ describe("Zotero.Sync.Runner", function () {
 		});
 		
 		
+		it("should show a button in error panel to select a too-long note", function* () {
+			win = yield loadZoteroPane();
+			var doc = win.document;
+			
+			var text = "".padStart(256, "a");
+			var item = yield createDataObject('item', { itemType: 'note', note: text });
+			
+			setResponse('keyInfo.fullAccess');
+			setResponse('userGroups.groupVersions');
+			setResponse('groups.ownerGroup');
+			setResponse('groups.memberGroup');
+			
+			server.respond(function (req) {
+				if (req.method == "POST" && req.url == baseURL + "users/1/items") {
+					req.respond(
+						200,
+						{
+							"Last-Modified-Version": 5
+						},
+						JSON.stringify({
+							successful: {},
+							success: {},
+							unchanged: {},
+							failed: {
+								"0": {
+									code: 413,
+									message: `Note ${Zotero.Utilities.ellipsize(text, 100)} too long`
+								}
+							}
+						})
+					);
+				}
+			});
+			
+			yield runner.sync({ libraries: [Zotero.Libraries.userLibraryID] });
+			
+			var errorIcon = doc.getElementById('zotero-tb-sync-error');
+			assert.isFalse(errorIcon.hidden);
+			errorIcon.click();
+			var panel = win.document.getElementById('zotero-sync-error-panel');
+			assert.include(panel.innerHTML, text.substr(0, 10));
+			var buttons = panel.getElementsByTagName('button');
+			assert.lengthOf(buttons, 1);
+			assert.include(buttons[0].label, Zotero.getString('pane.items.showItemInLibrary'));
+		});
+		
+		
 		// TODO: Test multiple long tags and tags across libraries
 		describe("Long Tag Fixer", function () {
 			it("should split a tag", function* () {