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

  1. Populate or load the relation before indexing: use the loader API (`with(child::Entity)`) or assign via `.replace(models)`/`.append(model)`.
  2. Check the state first with a match on `ActiveHasMany::NotSet` and handle the empty case instead of indexing.
  3. Use `try_index`/fallible accessors or `as_slice().get(index)` if available, handling `None`.
  4. 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

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


AI-assisted analysis of SeaQL/sea-orm@e29bcd1b41 (2026-09-10). Data as JSON: /api/errors/8b3a8e712e7386c2. Report an issue: GitHub.