TeamNewPipe/NewPipe · warning · InvalidJsonException
JSON doesn't contain "tabs" array
Error message
JSON doesn't contain "tabs" array
What it means
Thrown by TabsJsonHelper.getTabsFromJson when the parsed top-level JSON object does not contain the "tabs" key (JSON_TABS_ARRAY_KEY). The method expects an object with a tabs array; absence of that key is treated as invalid tab configuration and raises an InvalidJsonException. Note: a null/empty input string returns default tabs instead, so this only fires for a non-empty JSON lacking the key.
Source
Thrown at app/src/main/java/org/schabi/newpipe/settings/tabs/TabsJsonHelper.java:52
* {@link #getDefaultTabs fallback list} will be returned.
* <p>
* Tabs with invalid ids (i.e. not in the {@link Tab.Type} enum) will be ignored.
*
* @param tabsJson a JSON string got from {@link #getJsonToSave(List)}.
* @return a list of {@link Tab tabs}.
* @throws InvalidJsonException if the JSON string is not valid
*/
public static List<Tab> getTabsFromJson(@Nullable final String tabsJson)
throws InvalidJsonException {
if (tabsJson == null || tabsJson.isEmpty()) {
return getDefaultTabs();
}
try {
final JsonObject outerJsonObject = JsonParser.object().from(tabsJson);
if (!outerJsonObject.has(JSON_TABS_ARRAY_KEY)) {
throw new InvalidJsonException("JSON doesn't contain \"" + JSON_TABS_ARRAY_KEY
+ "\" array");
}
final JsonArray tabsArray = outerJsonObject.getArray(JSON_TABS_ARRAY_KEY, null);
final var returnTabs = tabsArray.streamAsJsonObjects()
.map(Tab::from)
.filter(Objects::nonNull)
.collect(Collectors.toUnmodifiableList());
return returnTabs.isEmpty() ? getDefaultTabs() : returnTabs;
} catch (final JsonParserException e) {
throw new InvalidJsonException(e);
}
}
/**
* Get a JSON representation from a list of tabs.View on GitHub (pinned to 9e8be09156)
Solutions
- Catch InvalidJsonException and fall back to getDefaultTabs() so the app still launches with sane defaults, then let the user reconfigure.
- Clear/reset the tabs preference to restore the default tabs JSON.
- Validate the JSON structure (presence of the "tabs" key) before persisting it, and write atomically to avoid truncated/corrupt saves.
- Add a migration step that upgrades older JSON shapes to the current {"tabs":[...]} structure.
Example fix
// before
try {
return TabsJsonHelper.getTabsFromJson(json);
} catch (final InvalidJsonException e) {
throw e;
}
// after
try {
return TabsJsonHelper.getTabsFromJson(json);
} catch (final InvalidJsonException e) {
return TabsJsonHelper.getDefaultTabs();
} Defensive patterns
Strategy: try-catch
Validate before calling
final JsonObject obj = JsonParser.object().from(tabsJson);
if (!obj.has("tabs")) {
// return default tabs instead of throwing
} Try / catch
try {
return TabsJsonHelper.getTabsFromJson(tabsJson);
} catch (final InvalidJsonException e) {
return TabsJsonHelper.getDefaultTabs();
} Prevention
- Validate the JSON shape (has "tabs" key) before persisting it.
- Write tab preferences atomically to avoid truncated/corrupt saves.
- Catch InvalidJsonException and fall back to default tabs so the app always launches.
- Add a migration for older JSON shapes when the schema changes.
When it happens
Trigger: getTabsFromJson is called with a non-empty JSON string whose top-level object has no "tabs" field. JsonParser.object().from succeeds (valid JSON) but outerJsonObject.has("tabs") is false.
Common situations: Corrupted or hand-edited tab settings JSON where the "tabs" key was removed or renamed, a migration/schema change that left an outdated JSON shape, or a partial write that saved a different object structure. The stored tabs preference (in SharedPreferences) got into an inconsistent state.
Related errors
AI-assisted analysis of TeamNewPipe/NewPipe@9e8be09156 (2026-08-14).
Data as JSON: /api/errors/837ebcda10856972.
Report an issue: GitHub.