yuliskov/SmartTube · error · IllegalArgumentException

Invalid row %s

Error message

Invalid row %s

What it means

BrowseSectionFragmentFactory maps each browse page row to a fragment via a switch on the section type carried by SectionHeaderItem (TYPE_ROW, TYPE_GRID, TYPE_SHORTS_GRID, TYPE_SETTINGS_GRID, TYPE_MULTI_GRID, TYPE_ERROR). When the resolved mFragmentType matches no case, fragment stays null and createFragment throws IllegalArgumentException('Invalid row ' + rowObj). It means a row arrived whose section type this factory cannot map to a page fragment.

Source

Thrown at smarttubetv/src/main/java/com/liskovsoft/smartyoutubetv2/tv/ui/browse/BrowseSectionFragmentFactory.java:95

                fragment = new MultiVideoGridFragment();
                break;
            case BrowseSection.TYPE_ERROR:
                fragment = new ErrorDialogFragment((ErrorFragmentData) ((SectionHeaderItem) header).getSection().getData());
                break;
        }

        if (fragment != null) {
            mCurrentFragment = fragment;

            runListeners(row);

            setCurrentFragmentItemIndex(mSelectedItemIndex);
            selectCurrentFragmentItem(mSelectedItem);

            return fragment;
        }

        throw new IllegalArgumentException(String.format("Invalid row %s", rowObj));
    }

    public void updateCurrentFragment(SettingsGroup group) {
        if (group == null) {
            return;
        }

        if (mCurrentFragment == null) {
            Log.e(TAG, "Page row fragment not initialized for group: " + group.getTitle());
            return;
        }

        if (mCurrentFragment instanceof SettingsSection) {
            ((SettingsSection) mCurrentFragment).update(group);
        } else {
            Log.e(TAG, "updateFragment: Page group fragment has incompatible type: " + mCurrentFragment.getClass().getSimpleName());
        }
    }

View on GitHub (pinned to 3de8d90593)

Solutions

  1. Log the failing row's runtime class and header type (rowObj plus ((SectionHeaderItem) header).getType()) to identify the unmapped value.
  2. Add a switch case in BrowseSectionFragmentFactory for the new type returning the right fragment, or map it to the closest existing grid type.
  3. Filter out rows with unknown section types before they reach the factory (in the rows-builder that creates PageRows).
  4. Check the upstream browsing structure/parser that produced the row for a malformed or future-version payload.

Example fix

// before — every page row reaches the factory; unknown type throws
 Object rowObj = ...;
 Fragment page = factory.createFragment(rowObj); // IllegalArgumentException: Invalid row ...

// after (data side) — only create PageRows for types the factory maps
 if (Arrays.asList(BrowseSection.TYPE_ROW, BrowseSection.TYPE_GRID,
         BrowseSection.TYPE_SHORTS_GRID, BrowseSection.TYPE_SETTINGS_GRID,
         BrowseSection.TYPE_MULTI_GRID, BrowseSection.TYPE_ERROR)
         .contains(headerItem.getType())) {
     rowsAdapter.add(new PageRow(headerItem));
 }

// after (factory side, if you own it) — default instead of throw
 default:
     Log.w(TAG, "Unhandled section type: " + mFragmentType);
     fragment = new VideoGridFragment();
Defensive patterns

Strategy: type-guard

Validate before calling

boolean isSupportedSectionType(int type) {
    switch (type) {
        case BrowseSection.TYPE_ROW:
        case BrowseSection.TYPE_GRID:
        case BrowseSection.TYPE_SHORTS_GRID:
        case BrowseSection.TYPE_SETTINGS_GRID:
        case BrowseSection.TYPE_MULTI_GRID:
        case BrowseSection.TYPE_ERROR:
            return true;
        default:
            return false;
    }
}

if (isSupportedSectionType(headerItem.getType())) {
    rowsAdapter.add(new PageRow(headerItem));
}

Type guard

static boolean isRenderableRow(Object rowObj) {
    if (!(rowObj instanceof Row)) return false;
    HeaderItem h = ((Row) rowObj).getHeaderItem();
    return h instanceof SectionHeaderItem
            && isSupportedSectionType(((SectionHeaderItem) h).getType());
}

Try / catch

try {
    Fragment page = factory.createFragment(rowObj);
} catch (IllegalArgumentException e) { // Invalid row %s
    Log.w(TAG, "Skipping unmapped row: " + rowObj, e);
    // fall back to a grid page for this header
}

Prevention

When it happens

Trigger: A SectionHeaderItem whose getType() returns a BrowseSection constant outside the switch (a newly added or removed section type); a row whose header is not a SectionHeaderItem while the previously sticky mFragmentType was also invalid — note mFragmentType persists across calls because the factory is re-used per header change.

Common situations: App/channel data introduces a new section type (e.g., a new shelf) that the installed build's factory does not know; version skew between the data producer and the UI; custom rows injected into the browse rows list; refactor renaming BrowseSection constants leaving a stale value in persisted state.

Related errors


AI-assisted analysis of yuliskov/SmartTube@3de8d90593 (2026-08-22). Data as JSON: /api/errors/ac79396ee5095630. Report an issue: GitHub.