material-components/material-components-android · error · IllegalArgumentException

Tab not attached to a TabLayout

Error message

Tab not attached to a TabLayout

What it means

Tab.setIcon(@DrawableRes int) needs the owning TabLayout to resolve the resource, so it requires tab.parent != null (set only when the tab is added via addTab). Calling it on a tab created by newTab() but not yet added throws IllegalArgumentException.

Source

Thrown at lib/java/com/google/android/material/tabs/TabLayout.java:2325

      this.icon = icon;
      if ((parent.tabGravity == GRAVITY_CENTER) || parent.mode == MODE_AUTO) {
        parent.updateTabViews(true);
      }
      updateView();
      return this;
    }

    /**
     * Set the icon displayed on this tab.
     *
     * @param resId A resource ID referring to the icon that should be displayed
     * @return The current instance for call chaining
     */
    @NonNull
    @CanIgnoreReturnValue
    public Tab setIcon(@DrawableRes int resId) {
      if (parent == null) {
        throw new IllegalArgumentException("Tab not attached to a TabLayout");
      }
      return setIcon(AppCompatResources.getDrawable(parent.getContext(), resId));
    }

    /**
     * Set the text displayed on this tab. Text may be truncated if there is not room to display the
     * entire string.
     *
     * @param text The text to display
     * @return The current instance for call chaining
     */
    @NonNull
    @CanIgnoreReturnValue
    public Tab setText(@Nullable CharSequence text) {
      if (TextUtils.isEmpty(contentDesc) && !TextUtils.isEmpty(text)) {
        // If no content description has been set, use the text as the content description of the
        // TabView. If the text is null, don't update the content description.
        view.setContentDescription(text);

View on GitHub (pinned to ac7e18efee)

Solutions

  1. Reorder so addTab(tab) happens before setIcon(resId).
  2. Or use the Drawable overload, which does not need the parent: setIcon(AppCompatResources.getDrawable(context, resId)).

Example fix

// before
Tab tab = tabLayout.newTab().setIcon(R.drawable.ic_home); // throws
 tabLayout.addTab(tab);

// after
Tab tab = tabLayout.newTab();
tabLayout.addTab(tab);
tab.setIcon(R.drawable.ic_home);
Defensive patterns

Strategy: validation

Validate before calling

// No public isAttached(): enforce order instead.
Tab tab = tabLayout.newTab();
// Correct pattern: attach first, then resource-based setters.
tabLayout.addTab(tab);
if (resId != 0) tab.setIcon(resId);

Try / catch

try { tab.setIcon(resId); } catch (IllegalArgumentException e) { /* tab not attached yet: add it first */ tabLayout.addTab(tab); tab.setIcon(resId); }

Prevention

When it happens

Trigger: Building a tab fluently but calling setIcon(resId) before tabLayout.addTab(tab): tabLayout.newTab().setIcon(R.drawable.ic_x).setText(...), then addTab.

Common situations: Builder-style chains written before the addTab call; porting code that used the Drawable overload (which works unattached) to the resource overload.

Related errors


AI-assisted analysis of material-components/material-components-android@ac7e18efee (2026-08-14). Data as JSON: /api/errors/1274e4b88ab09fab. Report an issue: GitHub.