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
- Change the setting field to a supported type: ObservableList/ObservableMap/ObservableSet or the corresponding *Property.
- Extend the deserializer's if/else chain to handle the new observable field type.
- If this error appears after a version change, update HMCL to a build that supports the field type.
- 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
- Use only ObservableList/Map/Set (or their *Property wrappers) for settings fields.
- Update the deserializer's type chain whenever adding a new observable field type.
- Test config round-trips after adding settings fields.
- Keep HMCL versions in sync with the config format that wrote the file.
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
- Cannot deserialize
- Config is not an object:
- Invalid JSON schema
- Json object cannot be null.
- json.toString()
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)