theonedev/onedev · error · java.lang.IllegalStateException

Tried to remove validator that was not previously added. Mak

Error message

Tried to remove validator that was not previously added. Make sure your validator's equals() implementation is sufficient

What it means

FormComponent.remove(IValidator) throws IllegalStateException when no equal validator is currently registered on the component. Wicket locates the validator via equals(), so an unequal but 'logically same' validator cannot be removed.

Source

Thrown at server-core/src/main/java/org/apache/wicket/markup/html/form/FormComponent.java:575

				break;
			}
			else if (behavior instanceof ValidatorAdapter)
			{
				if (((ValidatorAdapter<?>)behavior).getValidator().equals(validator))
				{
					match = behavior;
					break;
				}
			}
		}

		if (match != null)
		{
			remove(match);
		}
		else
		{
			throw new IllegalStateException(
				"Tried to remove validator that was not previously added. "
					+ "Make sure your validator's equals() implementation is sufficient");
		}
		return this;
	}

	/**
	 * Adds a validator to this form component.
	 * 
	 * @param validators
	 *            The validator(s) to be added
	 * @return This
	 * @throws IllegalArgumentException
	 *             if validator is null
	 * @see IValidator
	 */
	@SafeVarargs
	public final FormComponent<T> add(final IValidator<? super T>... validators)

View on GitHub (pinned to d44925c47c)

Solutions

  1. Keep a reference to the exact validator instance you added and remove that same instance
  2. Override equals() (and hashCode()) on custom validators so logically equal validators match
  3. Guard removal with a check, or add the validator once in onInitialize and toggle behavior another way
  4. Use component.getValidators() to confirm what is actually registered before removing

Example fix

// before
form.add(new LengthValidator(5));
form.remove(new LengthValidator(5)); // different instance, no equals
// after
private final LengthValidator len = new LengthValidator(5);
form.add(len);
...
form.remove(len); // same instance
Defensive patterns

Strategy: validation

Validate before calling

if (formComponent.getValidators().stream().noneMatch(v -> v.equals(validator))) {
    // do not call remove(validator)
}

Try / catch

try {
    formComponent.remove(validator);
} catch (IllegalStateException e) {
    log.debug("Validator not present, skipping");
}

Prevention

When it happens

Trigger: Calling formComponent.remove(validator) for a validator instance that was never added, was already removed, or whose equals() does not match the added instance (e.g. new instance each time without overriding equals).

Common situations: Creating a fresh validator object (e.g. new StringValidator(...)) and trying to remove it while the component holds a different instance; conditional add/remove logic where add was skipped; validators added in onInitialize but removal attempted before initialize.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06). Data as JSON: /api/errors/d95d049070c89fa5. Report an issue: GitHub.