HMCL-dev/HMCL · error · JsonParseException

Config is not an object:

Error message

Config is not an object: 

What it means

The custom Gson deserializer for ObservableSetting subclasses requires the incoming JSON to be a JSON object, since each key is mapped to an ObservableField of the setting instance. When the JSON element is present but is an array, string, number, boolean, or other non-object value, the deserializer aborts with this JsonParseException before creating the instance.

Solutions

  1. Inspect the JSON input and ensure the value at that position is a JSON object with keys matching the ObservableSetting fields.
  2. Validate the JSON shape (e.g. element.isJsonObject()) before handing it to Gson.
  3. Restore the config file from backup or regenerate it if it was corrupted or hand-mangled.

Example fix

// before
gson.fromJson("[1,2,3]", MySetting.class); // throws 'Config is not an object'

// after
JsonElement el = JsonParser.parseString(raw);
if (el.isJsonObject()) {
    MySetting s = gson.fromJson(el, MySetting.class);
}
Defensive patterns

Strategy: type-guard

Validate before calling

JsonElement el = JsonParser.parseString(raw);
if (!el.isJsonObject()) throw new IllegalArgumentException("Expected JSON object for config");

Type guard

static boolean isSettingJson(JsonElement el) { return el != null && el.isJsonObject(); }

Try / catch

try { return gson.fromJson(raw, MySetting.class); } catch (JsonParseException e) { log.error("Bad config shape", e); return defaultSetting(); }

Prevention

When it happens

Trigger: Gson deserializes a JSON document whose top-level (or nested, for a field of an ObservableSetting type) value is not an object — e.g. a JSON array, bare string, or number is fed to the adapter's deserialize() method.

Common situations: Hand-edited or corrupted config files where the settings object was replaced by an array or scalar; an API or tool emitting the wrong shape; deserializing the wrong type into an ObservableSetting-typed field.

Related errors


AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10). Data as JSON: /api/errors/3e7ec70d82349afb. Report an issue: GitHub.

Appendix: source

Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/util/gson/ObservableSetting.java:369

            JsonObject result = new JsonObject();
            for (var field : fields) {
                Observable observable = field.get(setting);
                if (setting.tracker.isDirty(observable)) {
                    field.serialize(result, setting, context);
                }
            }
            setting.unknownFields.forEach(result::add);
            return result;
        }

        @Override
        public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
            if (json == null || json.isJsonNull())
                return null;

            if (!json.isJsonObject())
                throw new JsonParseException("Config is not an object: " + json);

            T setting = createInstance();
            @SuppressWarnings("unchecked")
            var fields = (List<ObservableField<T>>) FIELDS.get(setting.getClass());

            var values = new LinkedHashMap<>(json.getAsJsonObject().asMap());
            for (ObservableField<T> field : fields) {
                JsonElement value = values.remove(field.getSerializedName());
                if (value == null) {
                    for (String alternateName : field.getAlternateNames()) {
                        value = values.remove(alternateName);
                        if (value != null)
                            break;
                    }
                }

                if (value != null) {
                    setting.tracker.markDirty(field.get(setting));

View on GitHub (pinned to 24702dc5a0)