theonedev/onedev · error · ValidationException

No value extractor found for type {type}.

Error message

No value extractor found for type {type}.

What it means

Hibernate Validator throws this when cascaded validation (@Valid) reaches a container-typed value (Optional, List, Map, custom generic) but no ValueExtractor is registered that knows how to unwrap that type parameter. The framework cannot descend into the container, so it fails fast instead of silently skipping element validation.

Source

Thrown at server-core/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java:706

		validateInContext( validationContext, cascadedValueContext, validationOrder );
	}

	private void validateCascadedContainerElementsForCurrentGroup(Object value, BaseBeanValidationContext<?> validationContext, ValueContext<?, ?> valueContext,
			List<ContainerCascadingMetaData> containerElementTypesCascadingMetaData) {
		for ( ContainerCascadingMetaData cascadingMetaData : containerElementTypesCascadingMetaData ) {
			if ( !cascadingMetaData.isMarkedForCascadingOnAnnotatedObjectOrContainerElements() ) {
				continue;
			}

			ValueExtractorDescriptor extractor = valueExtractorManager.getMaximallySpecificAndRuntimeContainerElementCompliantValueExtractor(
					cascadingMetaData.getEnclosingType(),
					cascadingMetaData.getTypeParameter(),
					value.getClass(),
					cascadingMetaData.getValueExtractorCandidates()
			);

			if ( extractor == null ) {
				throw LOG.getNoValueExtractorFoundForTypeException( cascadingMetaData.getEnclosingType(), cascadingMetaData.getTypeParameter(), value.getClass() );
			}

			CascadingValueReceiver receiver = new CascadingValueReceiver( validationContext, valueContext, cascadingMetaData );
			ValueExtractorHelper.extractValues( extractor, value, receiver );
		}
	}

	private class CascadingValueReceiver implements ValueExtractor.ValueReceiver {

		private final BaseBeanValidationContext<?> validationContext;
		private final ValueContext<?, ?> valueContext;
		private final ContainerCascadingMetaData cascadingMetaData;

		public CascadingValueReceiver(BaseBeanValidationContext<?> validationContext, ValueContext<?, ?> valueContext, ContainerCascadingMetaData cascadingMetaData) {
			this.validationContext = validationContext;
			this.valueContext = valueContext;
			this.cascadingMetaData = cascadingMetaData;
		}

View on GitHub (pinned to d44925c47c)

Solutions

  1. Add an @ExtractValue-annotated method (or field) to the custom container type so the framework knows which type parameter holds the validated values.
  2. Register a custom ValueExtractor for the container type via META-INF/services/javax.validation.valueextraction.ValueExtractor or ConstraintValidatorFactory/parameterized extractor registration.
  3. Remove @Valid from the field if element validation is not intended.
  4. Check that the same Hibernate Validator version is on the classpath at runtime as at compile time (mixed versions break extractor resolution).

Example fix

// before
class Result<T> { T value; }
@Valid Result<@NotNull String> result; // throws at validate()

// after
class Result<T> {
    T value;
    @ExtractValue
    public T getValue() { return value; }
}
@Valid Result<@NotNull String> result;
Defensive patterns

Strategy: validation

Validate before calling

// before validate(), ensure the container type has an extractor
boolean hasExtractor(Class<?> containerType, ValueExtractorRepository repo) {
    return repo.getValueExtractorCandidates(containerType, TypeUseKind.CONTAINER_ELEMENT)
               .size() > 0;
}

Type guard

static <T> boolean isSupportedContainer(Object v) {
    return v instanceof Collection || v instanceof Map || v instanceof Optional
        || v.getClass().isAnnotationPresent(ExtractValue.class)
        || java.util.Arrays.stream(v.getClass().getDeclaredMethods())
            .anyMatch(m -> m.isAnnotationPresent(ExtractValue.class));
}

Try / catch

try {
    validator.validate(bean);
} catch (ValidationException e) {
    if (e.getMessage().contains("No value extractor found")) {
        // register extractor or drop @Valid on that field, then retry
    } else throw e;
}

Prevention

When it happens

Trigger: Validating a bean with @Valid on a field/parameter whose declared type is a generic container (Optional<T>, List<T>, Map<K,V>, or a custom container) for which no matching ValueExtractor exists in the registered ValueExtractorRepository — typically a custom container type without a @ExtractValue annotated method or a registered extractor.

Common situations: Upgrading Hibernate Validator (extractor discovery rules changed between 6.x versions), using Optional/Collection wrappers from third-party libraries, or defining custom generic wrapper types (e.g. a Page<T> or Result<T> class) annotated with @Valid without registering an extractor.

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 theonedev/onedev@d44925c47c (2026-09-06). Data as JSON: /api/errors/86360db475577e7a. Report an issue: GitHub.