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.

Solutions

  1. Catch InvalidJsonException and fall back to getDefaultTabs() so the app still launches with sane defaults, then let the user reconfigure.
  2. Clear/reset the tabs preference to restore the default tabs JSON.
  3. Validate the JSON structure (presence of the "tabs" key) before persisting it, and write atomically to avoid truncated/corrupt saves.
  4. 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

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.

Appendix: 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)