HMCL-dev/HMCL · error · JsonParseException

Unsupported field type:

Error message

Unsupported field type: 

What it means

ObservableSetting's field deserializer copies deserialized values into JavaFX observable properties (ListProperty/MapProperty/SetProperty or plain Observable collections). This JsonParseException is thrown when a field's runtime value is an observable type the code does not support.

Solutions

  1. Change the setting field to a supported type: ObservableList/ObservableMap/ObservableSet or the corresponding *Property.
  2. Extend the deserializer's if/else chain to handle the new observable field type.
  3. If this error appears after a version change, update HMCL to a build that supports the field type.
  4. Remove or rename the offending field in the persisted settings JSON to skip it.

Example fix

// before
private final StringProperty name = new SimpleStringProperty(); // unsupported by this branch
// after
private final ObservableList<String> items = FXCollections.observableArrayList(); // supported
Defensive patterns

Strategy: type-guard

Validate before calling

if (!(fieldValue instanceof ObservableList
        || fieldValue instanceof ObservableMap
        || fieldValue instanceof ObservableSet
        || fieldValue instanceof SetProperty))
    throw new IllegalArgumentException("Unsupported observable field: " + fieldValue.getClass());

Type guard

static boolean isSupportedObservableField(Object v) {
    return v instanceof ObservableList || v instanceof ObservableMap || v instanceof ObservableSet || v instanceof SetProperty;
}

Try / catch

try {
    restoreSettings(json);
} catch (JsonParseException e) {
    log.error("Settings field type unsupported: {}", e.getMessage(), e);
}

Prevention

When it happens

Trigger: Restoring settings from JSON when a field's value is an Observable* subtype that is not a List/Map/Set property handled by the if/else chain (e.g. a new custom ObservableValue or BooleanProperty added to settings).

Common situations: Adding a new setting field of an unsupported observable type without updating the deserializer; version drift where a config written by a newer HMCL contains a field type this build cannot restore.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

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

            @SuppressWarnings({"unchecked"})
            @Override
            public void deserialize(T value, JsonElement element, JsonDeserializationContext context) {
                List<?> deserialized = context.deserialize(element, listType);
                Object fieldValue = get(value);

                if (fieldValue instanceof ListProperty) {
                    ((ListProperty<Object>) fieldValue).set(FXCollections.observableList((List<Object>) deserialized));
                } else if (fieldValue instanceof ObservableList) {
                    ((ObservableList<Object>) fieldValue).setAll(deserialized);
                } else if (fieldValue instanceof SetProperty) {
                    ((SetProperty<Object>) fieldValue).set(FXCollections.observableSet(new HashSet<>(deserialized)));
                } else if (fieldValue instanceof ObservableSet) {
                    ObservableSet<Object> set = (ObservableSet<Object>) fieldValue;
                    set.clear();
                    set.addAll(deserialized);
                } else {
                    throw new JsonParseException("Unsupported field type: " + fieldValue.getClass());
                }
            }
        }

        private static final class MapField<T> extends ObservableField<T> {
            private final Type mapType;

            MapField(String serializedName, List<String> alternate, VarHandle varHandle, Type mapType) {
                super(serializedName, alternate, varHandle);
                this.mapType = mapType;
            }

            @Override
            public void serialize(JsonObject result, T value, JsonSerializationContext context) {
                result.add(getSerializedName(), context.serialize(get(value), mapType));
            }

            @SuppressWarnings({"unchecked"})

View on GitHub (pinned to 24702dc5a0)