{"record":{"id":"ac79396ee5095630","repo":"yuliskov/SmartTube","slug":"invalid-row-s","errorCode":null,"errorMessage":"Invalid row %s","messagePattern":"Invalid row (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"smarttubetv/src/main/java/com/liskovsoft/smartyoutubetv2/tv/ui/browse/BrowseSectionFragmentFactory.java","lineNumber":95,"sourceCode":"                fragment = new MultiVideoGridFragment();\r\n                break;\r\n            case BrowseSection.TYPE_ERROR:\r\n                fragment = new ErrorDialogFragment((ErrorFragmentData) ((SectionHeaderItem) header).getSection().getData());\r\n                break;\r\n        }\r\n\r\n        if (fragment != null) {\r\n            mCurrentFragment = fragment;\r\n\r\n            runListeners(row);\r\n\r\n            setCurrentFragmentItemIndex(mSelectedItemIndex);\r\n            selectCurrentFragmentItem(mSelectedItem);\r\n\r\n            return fragment;\r\n        }\r\n\r\n        throw new IllegalArgumentException(String.format(\"Invalid row %s\", rowObj));\r\n    }\r\n\r\n    public void updateCurrentFragment(SettingsGroup group) {\r\n        if (group == null) {\r\n            return;\r\n        }\r\n\r\n        if (mCurrentFragment == null) {\r\n            Log.e(TAG, \"Page row fragment not initialized for group: \" + group.getTitle());\r\n            return;\r\n        }\r\n\r\n        if (mCurrentFragment instanceof SettingsSection) {\r\n            ((SettingsSection) mCurrentFragment).update(group);\r\n        } else {\r\n            Log.e(TAG, \"updateFragment: Page group fragment has incompatible type: \" + mCurrentFragment.getClass().getSimpleName());\r\n        }\r\n    }\r","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/yuliskov/SmartTube/blob/3de8d90593e0166f012ca18cc3c7ba45bfd68ac4/smarttubetv/src/main/java/com/liskovsoft/smartyoutubetv2/tv/ui/browse/BrowseSectionFragmentFactory.java#L77-L113","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before — every page row reaches the factory; unknown type throws\n Object rowObj = ...;\n Fragment page = factory.createFragment(rowObj); // IllegalArgumentException: Invalid row ...\n\n// after (data side) — only create PageRows for types the factory maps\n if (Arrays.asList(BrowseSection.TYPE_ROW, BrowseSection.TYPE_GRID,\n         BrowseSection.TYPE_SHORTS_GRID, BrowseSection.TYPE_SETTINGS_GRID,\n         BrowseSection.TYPE_MULTI_GRID, BrowseSection.TYPE_ERROR)\n         .contains(headerItem.getType())) {\n     rowsAdapter.add(new PageRow(headerItem));\n }\n\n// after (factory side, if you own it) — default instead of throw\n default:\n     Log.w(TAG, \"Unhandled section type: \" + mFragmentType);\n     fragment = new VideoGridFragment();","handlingStrategy":"type-guard","validationCode":"boolean isSupportedSectionType(int type) {\n    switch (type) {\n        case BrowseSection.TYPE_ROW:\n        case BrowseSection.TYPE_GRID:\n        case BrowseSection.TYPE_SHORTS_GRID:\n        case BrowseSection.TYPE_SETTINGS_GRID:\n        case BrowseSection.TYPE_MULTI_GRID:\n        case BrowseSection.TYPE_ERROR:\n            return true;\n        default:\n            return false;\n    }\n}\n\nif (isSupportedSectionType(headerItem.getType())) {\n    rowsAdapter.add(new PageRow(headerItem));\n}","typeGuard":"static boolean isRenderableRow(Object rowObj) {\n    if (!(rowObj instanceof Row)) return false;\n    HeaderItem h = ((Row) rowObj).getHeaderItem();\n    return h instanceof SectionHeaderItem\n            && isSupportedSectionType(((SectionHeaderItem) h).getType());\n}","tryCatchPattern":"try {\n    Fragment page = factory.createFragment(rowObj);\n} catch (IllegalArgumentException e) { // Invalid row %s\n    Log.w(TAG, \"Skipping unmapped row: \" + rowObj, e);\n    // fall back to a grid page for this header\n}","preventionTips":["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."],"tags":["android","android-tv","leanback","fragment-factory","type-dispatch"],"backgroundTag":"unmapped-row-type","analyzedSha":"3de8d90593e0166f012ca18cc3c7ba45bfd68ac4","analyzedAt":"2026-08-22T09:19:26.383Z","schemaVersion":2},"datasetVersion":"2026-08-22T14:17:55.899Z"}