{"record":{"id":"54358079100a9a0f","repo":"material-components/material-components-android","slug":"tablayoutmediator-attached-before-viewpager2-has-a","errorCode":null,"errorMessage":"TabLayoutMediator attached before ViewPager2 has an adapter","messagePattern":"TabLayoutMediator attached before ViewPager2 has an adapter","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"lib/java/com/google/android/material/tabs/TabLayoutMediator.java","lineNumber":117,"sourceCode":"    this.smoothScroll = smoothScroll;\n    this.tabConfigurationStrategy = tabConfigurationStrategy;\n  }\n\n  /**\n   * Link the TabLayout and the ViewPager2 together. Must be called after ViewPager2 has an adapter\n   * set. To be called on a new instance of TabLayoutMediator or if the ViewPager2's adapter\n   * changes.\n   *\n   * @throws IllegalStateException If the mediator is already attached, or the ViewPager2 has no\n   *     adapter.\n   */\n  public void attach() {\n    if (attached) {\n      throw new IllegalStateException(\"TabLayoutMediator is already attached\");\n    }\n    adapter = viewPager.getAdapter();\n    if (adapter == null) {\n      throw new IllegalStateException(\n          \"TabLayoutMediator attached before ViewPager2 has an \" + \"adapter\");\n    }\n    attached = true;\n\n    // Add our custom OnPageChangeCallback to the ViewPager\n    onPageChangeCallback = new TabLayoutOnPageChangeCallback(tabLayout);\n    viewPager.registerOnPageChangeCallback(onPageChangeCallback);\n\n    // Now we'll add a tab selected listener to set ViewPager's current item\n    onTabSelectedListener = new ViewPagerOnTabSelectedListener(viewPager, smoothScroll);\n    tabLayout.addOnTabSelectedListener(onTabSelectedListener);\n\n    // Now we'll populate ourselves from the pager adapter, adding an observer if\n    // autoRefresh is enabled\n    if (autoRefresh) {\n      // Register our observer on the new adapter\n      pagerAdapterObserver = new PagerAdapterObserver();\n      adapter.registerAdapterDataObserver(pagerAdapterObserver);","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/material-components/material-components-android/blob/ac7e18efeefb331850c561faf9ab8bf81d27ba68/lib/java/com/google/android/material/tabs/TabLayoutMediator.java#L99-L135","documentation":"TabLayoutMediator.attach() reads viewPager.getAdapter() and throws IllegalStateException when it is null, because the mediator's callbacks and tab population are driven entirely by adapter callbacks. The doc contract states attach() must run after ViewPager2 has an adapter set.","triggerScenarios":"Calling new TabLayoutMediator(tabLayout, viewPager2, ...).attach() before viewPager2.setAdapter(adapter).","commonSituations":"Adapter set asynchronously (loaded from a ViewModel LiveData whose observer fires after attach); initialization-order mistakes in onViewCreated; adapter assignment behind a data-load callback while attach ran eagerly.","solutions":["Set the adapter first: viewPager2.setAdapter(adapter); then mediator.attach().","If the adapter arrives asynchronously, create and attach the mediator only after the adapter is set (e.g. inside the observer that delivers the adapter).","Alternatively use RecyclerView.Adapter state restoration carefully: keep mediator creation lazy until both pager and adapter are ready."],"exampleFix":"// before\nnew TabLayoutMediator(tabLayout, viewPager2, true, configurator).attach();\nviewPager2.setAdapter(adapter); // attach ran too early\n\n// after\nviewPager2.setAdapter(adapter);\nnew TabLayoutMediator(tabLayout, viewPager2, true, configurator).attach();","handlingStrategy":"validation","validationCode":"if (viewPager2.getAdapter() != null) {\n  new TabLayoutMediator(tabLayout, viewPager2, configurator).attach();\n} else {\n  // set adapter first, or defer attach until the adapter is delivered\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always viewPager2.setAdapter(...) before mediator.attach().","With async adapters, attach inside the callback that delivers the adapter.","Treat attach() as the last step of pager setup."],"tags":["tablayout","viewpager2","api-order","android"],"backgroundTag":null,"analyzedSha":"ac7e18efeefb331850c561faf9ab8bf81d27ba68","analyzedAt":"2026-08-14T15:35:35.735Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}