hibernate/hibernate-orm · error · IllegalStateException
Bag is not a list:
Error message
Bag is not a list:
What it means
PersistentBag can wrap any java.util.Collection; list-position operations go through bagAsList(), which requires the wrapped collection to actually be a List. If the bag was built around a non-List collection (for example a HashSet the application placed in the field), any list-style operation such as add(index, e) or get(index) throws IllegalStateException naming the offending collection class. The mapping says bag, the runtime wrapper is not list-backed, and list operations are therefore impossible.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/collection/spi/PersistentBag.java:94
*
* @param session The session
* @param coll The base elements.
*/
public PersistentBag(SharedSessionContractImplementor session, Collection<E> coll) {
super( session );
setCollection( coll );
setInitialized();
setDirectlyAccessible( true );
}
private void setCollection(Collection<E> bag) {
this.collection = bag;
this.bag = bag instanceof List<E> list ? list : null;
}
protected List<E> bagAsList() {
if ( bag == null ) {
throw new IllegalStateException( "Bag is not a list: " + collection.getClass().getName() );
}
return bag;
}
@Override
public boolean isWrapper(Object collection) {
return this.collection == collection;
}
@Override
public boolean empty() {
return collection.isEmpty();
}
@Override
public Iterator<E> entries(CollectionPersister persister) {
return collection.iterator();
}View on GitHub (pinned to fad1729dce)
Solutions
- Declare bag-mapped fields as List and initialize them with ArrayList so list operations stay valid
- If set semantics are wanted, map the association as a Set (PersistentSet) instead of a bag
- Never call indexed operations (get/add at index) on bag-mapped collections
- Match field initialization to mapping semantics: List/ArrayList for bags and identifier bags
Example fix
// before @OneToMany(mappedBy = "order", cascade = CascadeType.ALL) private Collection<OrderLine> lines = new HashSet<>(); // bag wrapped around a Set -> bagAsList() throws // after @OneToMany(mappedBy = "order", cascade = CascadeType.ALL) private List<OrderLine> lines = new ArrayList<>();
Defensive patterns
Strategy: type-guard
Validate before calling
// before any list-style call on a bag-mapped collection
if (!(order.getLines() instanceof List)) {
throw new IllegalStateException(
"lines is bag-mapped around a non-List collection; indexed access unsupported");
} Type guard
static boolean supportsIndexAccess(Collection<?> collection) {
return collection instanceof List;
} Prevention
- Initialize bag-mapped fields with ArrayList
- Match field type to mapping semantics (List for bag/list, Set for set, Map for map)
- Never cast persistent collections to List unless the mapping guarantees a List
When it happens
Trigger: A bag-mapped @OneToMany field declared as Collection but initialized with a non-List implementation (HashSet, ConcurrentLinkedQueue); Hibernate reuses the application-supplied collection when loading; later code casts or calls list operations (indexed add/get) on it.
Common situations: Bag-mapped collections initialized as new HashSet<>() by habit; switching collection implementations during refactoring; legacy <bag> mappings over set-like field types.
Related errors
- Bag is not a list:
- Bags don't have indexes :
- queued clear cannot be used with orphan delete
- Passed attribute name [%s] did not correspond to a collectio
- Duplicate collection definition '%s'
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/9aa05c3c9a573d27.
Report an issue: GitHub.