hibernate/hibernate-orm · error · AnnotationException

Unable to interpret CacheMode in named query hint: " + query

Error message

Unable to interpret CacheMode in named query hint: " + queryName

What it means

QueryHintDefinition.getCacheMode reads the org.hibernate.cacheMode hint string and calls CacheMode.interpretExternalSetting, which only accepts the enum names NORMAL, IGNORE, GET, PUT, REFRESH (case-insensitive). An unrecognized value throws a MappingException that getCacheMode wraps in AnnotationException, naming the query. Validation happens at bootstrap while the named query definition is initialized.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/boot/model/internal/QueryHintDefinition.java:139

	public Timeout getTimeoutRef() {
		final Integer timeoutSeconds = getTimeout();
		return timeoutSeconds == null ? null : Timeout.seconds( timeoutSeconds );
	}

	public boolean getCacheability() {
		return getBoolean( HibernateHints.HINT_CACHEABLE );
	}

	@Nullable
	public CacheMode getCacheMode() {
		final String value = getString( HibernateHints.HINT_CACHE_MODE );
		try {
			return value == null
					? null
					: CacheMode.interpretExternalSetting( value );
		}
		catch (Exception e) {
			throw new AnnotationException( "Unable to interpret CacheMode in named query hint: " + queryName, e );
		}
	}

	@Nullable
	public QueryFlushMode getFlushMode() {
		final String value = getString( HibernateHints.HINT_FLUSH_MODE );
		try {
			return value == null
					? null
					: FlushModeTypeHelper.queryFlushModeFromHint( value );
		}
		catch (MappingException e) {
			throw new AnnotationException( "Unable to interpret FlushMode in named query hint: " + queryName, e );
		}
	}

	@Nullable
	public LockMode getLockMode(String query) {

View on GitHub (pinned to fad1729dce)

Solutions

  1. Use one of the five CacheMode names: NORMAL, IGNORE, GET, PUT, REFRESH (e.g. "GET" to read from cache without putting).
  2. If you meant to pick a collection/entity caching strategy, that belongs on @Cache(usage = ...) or region config, not the cacheMode hint.
  3. Remove the hint if you only wanted default NORMAL behavior.

Example fix

// before
@QueryHint(name = "org.hibernate.cacheMode", value = "read-write")

// after
@QueryHint(name = "org.hibernate.cacheMode", value = "GET")
Defensive patterns

Strategy: validation

Validate before calling

@Test void cacheModeHintUsesEnumName() {
    Set<String> valid = Set.of("NORMAL", "IGNORE", "GET", "PUT", "REFRESH");
    for (QueryHint h : collectHints()) {
        if ("org.hibernate.cacheMode".equals(h.name()))
            assertTrue(valid.contains(h.value().toUpperCase(Locale.ROOT)),
                "cacheMode must be one of " + valid + ", was: " + h.value());
    }
}

Type guard

boolean isValidCacheModeHint(String v) {
    return Arrays.stream(CacheMode.values()).anyMatch(m -> m.name().equalsIgnoreCase(v));
}

Try / catch

try {
    metadata = sources.buildMetadata();
} catch (AnnotationException e) { // wraps CacheMode MappingException, names the query
    failBuild("Unreadable cacheMode hint: " + e.getMessage());
}

Prevention

When it happens

Trigger: @QueryHint(name = "org.hibernate.cacheMode", value = "read-write") — a cache concurrency-strategy name rather than a CacheMode; also "use_query_cache", "ALL", or any made-up label. Fires when binding the named query that carries the hint.

Common situations: Confusing CacheMode (NORMAL/GET/PUT/REFRESH/IGNORE) with cache concurrency strategies (read-only/read-write/transactional); copying second-level-cache region config values into the cacheMode hint; values pasted from persistence.xml shared-cache-mode docs.

Related errors


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/5ba58271fcf4444b. Report an issue: GitHub.