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
- Log the failing row's runtime class and header type (rowObj plus ((SectionHeaderItem) header).getType()) to identify the unmapped value.
- Add a switch case in BrowseSectionFragmentFactory for the new type returning the right fragment, or map it to the closest existing grid type.
- Filter out rows with unknown section types before they reach the factory (in the rows-builder that creates PageRows).
- 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
- Filter unknown section types when building PageRows, before the factory sees them.
- When adding a BrowseSection type, extend the factory switch in the same commit.
- Remember mFragmentType is sticky across calls — always set the type via SectionHeaderItem.
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
- VideoGridFragment doesn't attached to the activity
- Can't create root of SurfacePlaybackFragment
- Unknown Uri: ${uri}
- Can't find matched preference for type: ${category.type}
- Can't create PlaybackFragment: the context is null
AI-assisted analysis of yuliskov/SmartTube@3de8d90593 (2026-08-22).
Data as JSON: /api/errors/ac79396ee5095630.
Report an issue: GitHub.