HMCL-dev/HMCL · error · JsonParseException

profiles is null

Error message

profiles is null

What it means

Fires in HostOK.validate() when the deserialized Terracotta server response has a null profiles list. It is a structural guard over the config-server payload: a host report without its profiles array fails Gson parsing because the client cannot determine which Terracotta profile the host refers to. The input at fault is a JSON response missing the serialized 'profiles' field.

Solutions

  1. Include a non-null 'profiles' member (at least an empty list) in the state JSON
  2. Regenerate the state file via the terracotta flow
  3. Restore a known-good backup of the state file
  4. Catch JsonParseException and re-create the state with defaults

Example fix

// before
{"code":"AUTH-CODE"}
// after
{"code":"AUTH-CODE","profiles":[]}
Defensive patterns

Strategy: validation

Validate before calling

if (parsedState.get("profiles") == null || parsedState.get("profiles").isJsonNull()) { throw new IllegalArgumentException("state.profiles required"); }

Type guard

static boolean hasProfiles(JsonObject o) { return o.has("profiles") && !o.get("profiles").isJsonNull(); }

Try / catch

try { state.validate(); } catch (JsonParseException e) { /* restore backup or rebuild state */ }

Prevention

When it happens

Trigger: A terracotta state JSON deserialized without the profiles field — absent key, explicit null, or a truncated/older-format file.

Common situations: Hand-edited state files; schema changes between versions; partial writes leaving profiles out.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


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

Appendix: source

Thrown at HMCL/src/main/java/org/jackhuang/hmcl/terracotta/TerracottaState.java:194

        private final int profileIndex;

        @SerializedName("profiles")
        private final List<TerracottaProfile> profiles;

        HostOK(int port, int index, String state, String code, int profileIndex, List<TerracottaProfile> profiles) {
            super(port, index, state);
            this.code = code;
            this.profileIndex = profileIndex;
            this.profiles = profiles;
        }

        @Override
        public void validate() throws JsonParseException, TolerableValidationException {
            if (code == null) {
                throw new JsonParseException("code is null");
            }
            if (profiles == null) {
                throw new JsonParseException("profiles is null");
            }
        }

        public String getCode() {
            return code;
        }

        public List<TerracottaProfile> getProfiles() {
            return profiles;
        }

        @Override
        public boolean isForkOf(TerracottaState state) {
            return state instanceof HostOK hostOK && this.index - hostOK.index <= profileIndex;
        }
    }

    public static final class GuestConnecting extends Ready {

View on GitHub (pinned to 24702dc5a0)