facebook/docusaurus · error · Error

Can't select invalid tab value=${newValue}

Error message

Can't select invalid tab value=${newValue}

What it means

The selectValue function returned from the Tabs hook (and exposed via useTabs context) only accepts a value that exists in tabValues. It calls isValidValue and throws 'Can't select invalid tab value=...' to forbid programmatically activating a tab that does not exist. This guards internal invariants when consumers drive tab selection from external state.

Source

Thrown at packages/docusaurus-theme-common/src/utils/tabsUtils.tsx:274

  const valueToSync = (() => {
    const value = queryStringValue ?? storageValue;
    if (!isValidValue({value, tabValues})) {
      return null;
    }
    return value;
  })();
  // Sync in a layout/sync effect is important, for useScrollPositionBlocker
  // See https://github.com/facebook/docusaurus/issues/8625
  useIsomorphicLayoutEffect(() => {
    if (valueToSync) {
      setSelectedValue(valueToSync);
    }
  }, [valueToSync]);

  const selectValue = useCallback(
    (newValue: string) => {
      if (!isValidValue({value: newValue, tabValues})) {
        throw new Error(`Can't select invalid tab value=${newValue}`);
      }
      setSelectedValue(newValue);
      setQueryString(newValue);
      setStorageValue(newValue);
    },
    [setQueryString, setStorageValue, tabValues],
  );

  return {
    selectedValue,
    selectValue,
    tabValues,
    lazy: props.lazy ?? false,
    block: props.block ?? false,
  };
}

const TabsContext = createContext<TabsContextValue | null>(null);

View on GitHub (pinned to 3f483e80e3)

Solutions

  1. Guard the call: only invoke selectValue when tabValues.some(t => t.value === next).
  2. Refresh tabValues before selecting (ensure the target value is rendered first).
  3. If the value comes from URL/storage, rely on the built-in sync which already filters invalid values via isValidValue in valueToSync.

Example fix

// before
const {selectValue} = useTabs();
selectValue(maybeValue);
// after
const {selectValue, tabValues} = useTabs();
if (tabValues.some((t) => t.value === maybeValue)) {
  selectValue(maybeValue);
}
Defensive patterns

Strategy: type-guard

Validate before calling

const {selectValue, tabValues} = useTabs();
if (tabValues.some((t) => t.value === next)) {
  selectValue(next);
}

Type guard

const isSelectableTabValue = (
  v: string,
  tabValues: readonly {value: string}[],
): v is string => tabValues.some((t) => t.value === v);

Prevention

When it happens

Trigger: Call tabs.selectValue('linux') when no <TabItem value="linux"> exists; feed selectValue from a stale closure after tabValues changed asynchronously; sync from URL/localStorage with an out-of-date value list.

Common situations: External state machines driving Tabs; dynamically rendered tab lists where a value is removed while a selectValue call is in flight; tests calling selectValue with a literal not present in fixtures.

Related errors


AI-assisted analysis of facebook/docusaurus@3f483e80e3 (2026-08-12). Data as JSON: /api/errors/7a38e38dd120a73f. Report an issue: GitHub.