{"record":{"id":"7a38e38dd120a73f","repo":"facebook/docusaurus","slug":"can-t-select-invalid-tab-value-newvalue","errorCode":null,"errorMessage":"Can't select invalid tab value=${newValue}","messagePattern":"Can't select invalid tab value=(.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/docusaurus-theme-common/src/utils/tabsUtils.tsx","lineNumber":274,"sourceCode":"  const valueToSync = (() => {\n    const value = queryStringValue ?? storageValue;\n    if (!isValidValue({value, tabValues})) {\n      return null;\n    }\n    return value;\n  })();\n  // Sync in a layout/sync effect is important, for useScrollPositionBlocker\n  // See https://github.com/facebook/docusaurus/issues/8625\n  useIsomorphicLayoutEffect(() => {\n    if (valueToSync) {\n      setSelectedValue(valueToSync);\n    }\n  }, [valueToSync]);\n\n  const selectValue = useCallback(\n    (newValue: string) => {\n      if (!isValidValue({value: newValue, tabValues})) {\n        throw new Error(`Can't select invalid tab value=${newValue}`);\n      }\n      setSelectedValue(newValue);\n      setQueryString(newValue);\n      setStorageValue(newValue);\n    },\n    [setQueryString, setStorageValue, tabValues],\n  );\n\n  return {\n    selectedValue,\n    selectValue,\n    tabValues,\n    lazy: props.lazy ?? false,\n    block: props.block ?? false,\n  };\n}\n\nconst TabsContext = createContext<TabsContextValue | null>(null);","sourceCodeStart":256,"sourceCodeEnd":292,"githubUrl":"https://github.com/facebook/docusaurus/blob/3f483e80e326cc646b54b83d564b3f0c4881b9a6/packages/docusaurus-theme-common/src/utils/tabsUtils.tsx#L256-L292","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Guard the call: only invoke selectValue when tabValues.some(t => t.value === next).","Refresh tabValues before selecting (ensure the target value is rendered first).","If the value comes from URL/storage, rely on the built-in sync which already filters invalid values via isValidValue in valueToSync."],"exampleFix":"// before\nconst {selectValue} = useTabs();\nselectValue(maybeValue);\n// after\nconst {selectValue, tabValues} = useTabs();\nif (tabValues.some((t) => t.value === maybeValue)) {\n  selectValue(maybeValue);\n}","handlingStrategy":"type-guard","validationCode":"const {selectValue, tabValues} = useTabs();\nif (tabValues.some((t) => t.value === next)) {\n  selectValue(next);\n}","typeGuard":"const isSelectableTabValue = (\n  v: string,\n  tabValues: readonly {value: string}[],\n): v is string => tabValues.some((t) => t.value === v);","tryCatchPattern":null,"preventionTips":["Never call selectValue with a value that was not in tabValues at render time.","When tabValues are async/dynamic, await their population before driving selection.","Centralize external->tab syncing through one validated helper."],"tags":["tabs","react","state-management","context"],"backgroundTag":null,"analyzedSha":"3f483e80e326cc646b54b83d564b3f0c4881b9a6","analyzedAt":"2026-08-12T13:25:04.382Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}