different-ai/openwork · error · Error

Tab order must include every open tab.

Error message

Tab order must include every open tab.

What it means

reorderBrowserTabs validates that the proposed order array has exactly the same length as the current browserTabOrder; anything shorter or longer throws this error, since a reorder must be a full permutation of open tabs, not a partial or extended list.

Source

Thrown at apps/desktop/electron/browser-panel.mjs:682

    const tabsToClose = closedTabIds
      .map((tabId) => browserTabs.get(tabId))
      .filter(Boolean);
    hideBrowserView();
    browserTabs.clear();
    browserTabOrder = [];
    activeBrowserTabId = null;
    for (const tab of tabsToClose) {
      try { tab.view.webContents.close(); } catch { /* already destroyed */ }
    }
    sendToRenderer("openwork:browser:panel-closed");
    sendBrowserState();
    return closedTabIds;
  }

  function reorderBrowserTabs(tabIds) {
    const nextOrder = Array.isArray(tabIds) ? tabIds.map(String) : [];
    if (nextOrder.length !== browserTabOrder.length) {
      throw new Error("Tab order must include every open tab.");
    }
    if (new Set(nextOrder).size !== nextOrder.length) {
      throw new Error("Tab order must not contain duplicate tabs.");
    }
    const current = new Set(browserTabOrder);
    if (nextOrder.some((tabId) => !current.has(tabId))) {
      throw new Error("Tab order contains an unknown tab.");
    }
    browserTabOrder = nextOrder;
    sendBrowserState();
    return listBrowserTabs();
  }

  function sendBrowserState() {
    sendToRenderer("openwork:browser:state", browserStatePayload());
  }

  /**

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Fetch the current tab order and send all open tab IDs in the desired new sequence
  2. Build the reorder payload from a fresh listBrowserTabs() result rather than cached state
  3. Reconcile the client snapshot with the server state when tab counts disagree, then retry

Example fix

// before
reorderBrowserTabs([draggedTabId]);
// after
const order = listBrowserTabs().map(t => t.tabId);
reorderBrowserTabs([draggedTabId, ...order.filter(id => id !== draggedTabId)]);
Defensive patterns

Strategy: validation

Validate before calling

function canReorder(tabIds, currentOrder) {
  const next = Array.isArray(tabIds) ? tabIds.map(String) : [];
  return next.length === currentOrder.length;
}
if (!canReorder(ids, listBrowserTabs().map(t => t.tabId))) throw new Error('order must contain every open tab');

Type guard

function isFullPermutation(ids, current) {
  const a = ids.map(String), b = current.map(String);
  return a.length === b.length && new Set(a).size === a.length && a.every(id => new Set(b).has(id));
}

Try / catch

try {
  reorderBrowserTabs(order);
} catch (e) {
  if (e.message === 'Tab order must include every open tab.') {
    const fresh = listBrowserTabs().map(t => t.tabId);
    reorderBrowserTabs(order.filter(id => fresh.includes(id)).concat(fresh.filter(id => !order.includes(id))));
  } else throw e;
}

Prevention

When it happens

Trigger: Calling reorder with a subset of tab IDs (e.g. only the dragged tab), an empty array when tabs are open, or an array that includes extra IDs beyond the open count (possibly from a stale UI snapshot taken before/after another tab opened).

Common situations: UI sending only the moved tab's id; concurrent tab open/close racing the reorder request; client built the order from an outdated browser-state snapshot.

Related errors


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/f9d008aa90579128. Report an issue: GitHub.