{"record":{"id":"48df83fa36f9b637","repo":"different-ai/openwork","slug":"tab-order-must-not-contain-duplicate-tabs","errorCode":null,"errorMessage":"Tab order must not contain duplicate tabs.","messagePattern":"Tab order must not contain duplicate tabs\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/desktop/electron/browser-panel.mjs","lineNumber":685,"sourceCode":"    hideBrowserView();\n    browserTabs.clear();\n    browserTabOrder = [];\n    activeBrowserTabId = null;\n    for (const tab of tabsToClose) {\n      try { tab.view.webContents.close(); } catch { /* already destroyed */ }\n    }\n    sendToRenderer(\"openwork:browser:panel-closed\");\n    sendBrowserState();\n    return closedTabIds;\n  }\n\n  function reorderBrowserTabs(tabIds) {\n    const nextOrder = Array.isArray(tabIds) ? tabIds.map(String) : [];\n    if (nextOrder.length !== browserTabOrder.length) {\n      throw new Error(\"Tab order must include every open tab.\");\n    }\n    if (new Set(nextOrder).size !== nextOrder.length) {\n      throw new Error(\"Tab order must not contain duplicate tabs.\");\n    }\n    const current = new Set(browserTabOrder);\n    if (nextOrder.some((tabId) => !current.has(tabId))) {\n      throw new Error(\"Tab order contains an unknown tab.\");\n    }\n    browserTabOrder = nextOrder;\n    sendBrowserState();\n    return listBrowserTabs();\n  }\n\n  function sendBrowserState() {\n    sendToRenderer(\"openwork:browser:state\", browserStatePayload());\n  }\n\n  /**\n   * Attach the browser view to the main window.\n   * @param {object} bounds — { x, y, width, height }\n   * @param {object} [opts]","sourceCodeStart":667,"sourceCodeEnd":703,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/apps/desktop/electron/browser-panel.mjs#L667-L703","documentation":"reorderBrowserTabs rejects order arrays containing the same tab id twice, detected via Set size vs array length. Duplicates would make the permutation ambiguous, so the request is refused.","triggerScenarios":"Calling reorder with an array where one tabId appears two or more times — typically from a buggy drag-and-drop handler concatenating arrays or failing to remove the dragged item before reinserting it.","commonSituations":"Drag-and-drop logic double-inserting the dragged tab; merging a moved-tab list with the full list without deduping; client state corruption after rapid reorder calls.","solutions":["Deduplicate the payload (Array.from(new Set(ids))) before sending, after fixing the source bug","Fix the drag handler to remove the item from its old position before inserting at the target","Validate the constructed order client-side (unique ids, same length as open tabs) before the call"],"exampleFix":"// before\nreorderBrowserTabs([...ids, draggedId]);\n// after\nconst next = Array.from(new Set([draggedId, ...ids]));\nreorderBrowserTabs(next);","handlingStrategy":"validation","validationCode":"function hasNoDuplicates(ids) { return new Set(ids.map(String)).size === ids.length; }\nif (!hasNoDuplicates(order)) throw new Error('reorder payload contains duplicate tab ids');","typeGuard":"function isUniqueStringArray(v) { return Array.isArray(v) && new Set(v.map(String)).size === v.length; }","tryCatchPattern":"try {\n  reorderBrowserTabs(order);\n} catch (e) {\n  if (e.message === 'Tab order must not contain duplicate tabs.') {\n    reorderBrowserTabs(Array.from(new Set(order.map(String))));\n  } else throw e;\n}","preventionTips":["Fix drag-and-drop handlers to remove the dragged item before reinsertion","Deduplicate with new Set() before sending reorder payloads","Unit-test reorder construction with rapid consecutive drags","Validate uniqueness client-side before the call"],"tags":["validation","browser-tabs","duplicate-ids"],"backgroundTag":"duplicate-identifier-in-collection","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}