Skip to content

Validation and Lifecycle Hooks

Gustavo Malvestiti edited this page Aug 16, 2026 · 1 revision

Implement ConfigExtension on the config class to participate in the load/save/validate lifecycle:

@Config(name = "mymod")
public final class MyModConfig implements ConfigExtension {

    public int hudScale = 2;
    public String worldPreset = "default";

    @Override
    public void afterLoad() {
        // Fix up values after loading — clamp ranges, derive computed fields, etc.
        hudScale = Math.max(1, Math.min(8, hudScale));
    }

    @Override
    public void beforeSave() {
        // Normalize values before writing to disk, if needed.
        // Most configs leave this empty.
    }

    @Override
    public void validate(List<Violation> violations) {
        // Report constraint violations. Must be side-effect free.
        if (hudScale < 1 || hudScale > 8) {
            violations.add(Violation.of("hud-scale.range", "hudScale must be between 1 and 8, was " + hudScale));
        }
    }
}

Hook order and contract:

  • afterLoad runs after deserialization, before validation. Use it to normalize or clamp values.
  • validate runs after afterLoad, on every load and before every update is published. It must be side-effect free — correct values in afterLoad, report them in validate.
  • beforeSave runs immediately before serialization.

Handling validation failures:

Under FALLBACK update policy, violations come back on the UpdateResult:

UpdateResult result = holder.updateAndSave(config -> config.hudScale = 99);
if (!result.accepted()) {
    result.violations().forEach(v -> LOGGER.warn("{}: {}", v.id(), v.message()));
}

Under STRICT, the same violations ride on EasyConfigException.violations().

Validation on load: if the file on disk contains values that fail validate, the behavior depends on readFailurePolicy. Under FALLBACK, the file is backed up and defaults are written. Under STRICT, the load throws.

Clone this wiki locally