hibernate/hibernate-orm · error · MappingException

Non-terminal property path did not reference FetchableContai

Error message

Non-terminal property path did not reference FetchableContainer: " + navigablePath

What it means

When resolving a @FieldResult name that contains multiple dot-separated parts, SqlResultSetMappingDescriptor walks the entity metamodel part by part. Each intermediate part must resolve to a ModelPartContainer (an embeddable, association, or collection) so the walk can continue; when an intermediate part is a basic attribute, the remaining parts have nowhere to go and this MappingException is thrown with the NavigablePath reached so far. It is the annotation-side twin of error 749.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/boot/query/SqlResultSetMappingDescriptor.java:448

//		}

		@Nonnull
		@Override
		public FetchMemento resolve(@Nonnull ResultSetMappingResolutionContext resolutionContext) {
			final var entityMapping =
					resolutionContext.getMappingMetamodel()
							.getEntityDescriptor( entityName );
			final String rootPropertyPathPart = propertyPathParts[0];
			var subPart = entityMapping.findSubPart( rootPropertyPathPart, null );
			var navigablePath = rootNavigablePath( subPart, entityMapping, rootPropertyPathPart );
			for ( int i = 1; i < propertyPathParts.length; i++ ) {
				if ( subPart instanceof ModelPartContainer modelPartContainer ) {
					final String propertyPathPart = propertyPathParts[i];
					navigablePath = navigablePath.append( propertyPathPart );
					subPart = modelPartContainer.findSubPart( propertyPathPart, null );
				}
				else {
					throw new MappingException(
							"Non-terminal property path did not reference FetchableContainer: "
							+ navigablePath
					);
				}
			}
			return getFetchMemento( navigablePath, subPart );
		}

		private NavigablePath rootNavigablePath(
				@Nonnull ModelPart parentSubPart,
				@Nonnull EntityPersister entityMapping,
				@Nonnull String rootPropertyPathPart) {
			final var parentNavigableRole = parentSubPart.getNavigableRole().getParent();
			final var parentNavigablePath =
					!Objects.equals( parentNavigableRole, entityMapping.getNavigableRole() )
						&& parentNavigableRole.getLocalName().equals( ID_ROLE_NAME )
							// The attribute is defined in an ID class, append {id} to navigable path
							? new EntityIdentifierNavigablePath( this.navigablePath, null )

View on GitHub (pinned to fad1729dce)

Solutions

  1. Shorten the path to end at the basic attribute: @FieldResult(name = "address.zip", column = "ZIP").
  2. If nesting is intended, make the intermediate attribute an @Embeddable/component so it is a ModelPartContainer.
  3. Verify each intermediate segment against the entity's current mapped structure and fix stale names.

Example fix

// before: 'address.zip' is a basic String
@FieldResult(name = "address.zip.code", column = "ZIP")

// after
@FieldResult(name = "address.zip", column = "ZIP")
Defensive patterns

Strategy: validation

Validate before calling

// validate a dotted @FieldResult name against the metamodel before creating the EMF:
ManagedDomainType<?> t = metamodel.entity( entityClass );
String[] parts = fieldResult.name().split( "\\." );
for ( int i = 0; i < parts.length; i++ ) {
    Attribute<?, ?> attr = (Attribute<?, ?>) ( (ManagedDomainType<?>) t ).getAttribute( parts[i] );
    if ( attr == null ) throw new IllegalStateException( "Unknown segment '" + parts[i] + "' in " + fieldResult.name() );
    if ( i < parts.length - 1 && ! ( attr.getPersistentAttributeType() == PersistentAttributeType.EMBEDDED
            || attr.getPersistentAttributeType() == PersistentAttributeType.ELEMENT_COLLECTION
            || attr.getPersistentAttributeType() == PersistentAttributeType.MANY_TO_ONE
            || attr.getPersistentAttributeType() == PersistentAttributeType.ONE_TO_ONE ) ) {
        throw new IllegalStateException( "Segment '" + parts[i] + "' is basic; path too deep: " + fieldResult.name() );
    }
    if ( i < parts.length - 1 ) t = metamodel.embeddable( attr.getJavaType() );
}

Try / catch

catch ( MappingException e ) {
    if ( e.getMessage().startsWith( "Non-terminal property path did not reference FetchableContainer" ) ) {
        // shorten the @FieldResult name to end at the basic attribute, or model the middle segment as an embeddable
    }
}

Prevention

When it happens

Trigger: @FieldResult(name = "address.zip.code", column = "ZIP") where 'address.zip' is a plain String; a dotted path left over from when the attribute was an embeddable and is now basic; paths copied from another entity with a deeper component structure.

Common situations: Refactoring an embeddable into basic columns (or vice versa) without updating @SqlResultSetMapping; entity-specific mappings reused across similar entities; typos that make an intermediate segment resolve to a basic attribute.

Related errors


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