SeaQL/sea-orm · error
index out of bounds: the ActiveHasMany is NotSet (index: {in
Error message
index out of bounds: the ActiveHasMany is NotSet (index: {index}) What it means
The `Index<usize>` implementation for `ActiveHasMany<E>` panics when the relation state is `NotSet` — meaning no child models have been loaded or assigned yet — while code tries to read an element by index. The library cannot produce an element for a relation that was never populated, so it treats indexing as out of bounds.
Source
Thrown at sea-orm-sync/src/entity/active_model_ex.rs:430
}
}
impl<E: EntityTrait> From<ActiveHasMany<E>> for Option<Vec<E::ActiveModelEx>> {
fn from(value: ActiveHasMany<E>) -> Self {
match value {
ActiveHasMany::NotSet => None,
ActiveHasMany::Replace(models) | ActiveHasMany::Append(models) => Some(models),
}
}
}
impl<E: EntityTrait> Index<usize> for ActiveHasMany<E> {
type Output = E::ActiveModelEx;
fn index(&self, index: usize) -> &Self::Output {
match self {
ActiveHasMany::NotSet => {
panic!("index out of bounds: the ActiveHasMany is NotSet (index: {index})")
}
ActiveHasMany::Replace(models) | ActiveHasMany::Append(models) => models.index(index),
}
}
}
impl<E: EntityTrait> IndexMut<usize> for ActiveHasMany<E> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
match self {
ActiveHasMany::NotSet => {
panic!("index out of bounds: the ActiveHasMany is NotSet (index: {index})")
}
ActiveHasMany::Replace(models) | ActiveHasMany::Append(models) => {
models.index_mut(index)
}
}
}
}View on GitHub (pinned to e29bcd1b41)
Solutions
- Populate or load the relation before indexing: use the loader API (`with(child::Entity)`) or assign via `.replace(models)`/`.append(model)`.
- Check the state first with a match on `ActiveHasMany::NotSet` and handle the empty case instead of indexing.
- Use `try_index`/fallible accessors or `as_slice().get(index)` if available, handling `None`.
- Ensure the code path that was supposed to load children actually ran (don't skip the `with(...)` call).
Example fix
// before
let first = &post.comments[0]; // panics if comments is NotSet
// after
if let ActiveHasMany::Replace(models) | ActiveHasMany::Append(models) = &post.comments {
if let Some(first) = models.get(0) { /* ... */ }
} Defensive patterns
Strategy: type-guard
Validate before calling
// Guard against NotSet before indexing
let items: Option<&Vec<child::ActiveModelEx>> = match &post.children {
ActiveHasMany::Replace(m) | ActiveHasMany::Append(m) => Some(m),
ActiveHasMany::NotSet => None,
}; Type guard
fn as_loaded_slice<'a, E: EntityTrait>(r: &'a ActiveHasMany<E>) -> Option<&'a [E::ActiveModelEx]> {
match r { ActiveHasMany::Replace(m) | ActiveHasMany::Append(m) => Some(m), ActiveHasMany::NotSet => None }
} Try / catch
// Indexing panics; avoid try-catch, use the guard
match as_loaded_slice(&post.children).and_then(|m| m.get(0)) {
Some(first) => {},
None => {} // handle unset/empty relation
} Prevention
- Always eager-load has-many relations with .with(child::Entity) before reading them
- Check the relation state instead of assuming it is populated
- In tests/fixtures, explicitly replace() relations on new ActiveModels
When it happens
Trigger: `active_model.children[index]` (or `.index(i)`) where `children` is an `ActiveHasMany` still in `NotSet` state — i.e. the relation was never set via `.replace(...)`/`.append(...)` nor loaded from the database before indexing.
Common situations: Indexing a has-many relation before calling a load/`with(...)` fetch; constructing a new ActiveModel from scratch and assuming children exist; relying on a relation that a previous save path left untouched.
Related errors
- index out of bounds: the HasMany is Unloaded (index: {index}
- index out of bounds: the ActiveHasMany is NotSet (index: {in
- called `BelongsTo::unwrap()` on an `Unloaded` value
- called `BelongsTo::unwrap()` on a `Loaded(None)` value
- index out of bounds: the HasMany is Unloaded (index: {index}
AI-assisted analysis of SeaQL/sea-orm@e29bcd1b41 (2026-09-10).
Data as JSON: /api/errors/8b3a8e712e7386c2.
Report an issue: GitHub.