{"record":{"id":"9eb53ae9fbc35cc0","repo":"SeaQL/sea-orm","slug":"index-out-of-bounds-the-hasmany-is-unloaded-inde","errorCode":null,"errorMessage":"index out of bounds: the HasMany is Unloaded (index: {index})","messagePattern":"index out of bounds: the HasMany is Unloaded \\(index: (.+?)\\)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sea-orm-sync/src/entity/compound/has_many.rs","lineNumber":102,"sourceCode":"    }\n}\n\nimpl<E: EntityTrait> From<HasMany<E>> for Option<Vec<E::ModelEx>> {\n    fn from(value: HasMany<E>) -> Self {\n        match value {\n            HasMany::Loaded(models) => Some(models),\n            HasMany::Unloaded => None,\n        }\n    }\n}\n\nimpl<E: EntityTrait> Index<usize> for HasMany<E> {\n    type Output = E::ModelEx;\n\n    fn index(&self, index: usize) -> &Self::Output {\n        match self {\n            HasMany::Unloaded => {\n                panic!(\"index out of bounds: the HasMany is Unloaded (index: {index})\")\n            }\n            HasMany::Loaded(items) => items.index(index),\n        }\n    }\n}\n\nimpl<E: EntityTrait> IntoIterator for HasMany<E> {\n    type Item = E::ModelEx;\n    type IntoIter = std::vec::IntoIter<E::ModelEx>;\n\n    fn into_iter(self) -> Self::IntoIter {\n        match self {\n            HasMany::Loaded(models) => models.into_iter(),\n            HasMany::Unloaded => Vec::new().into_iter(),\n        }\n    }\n}\n","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/SeaQL/sea-orm/blob/e29bcd1b417c41a553b386fe94511d7c64a1c8ec/sea-orm-sync/src/entity/compound/has_many.rs#L84-L120","documentation":"The `Index<usize>` implementation for `HasMany<E>` (the loaded-model relation wrapper) panics when the relation is `Unloaded` — no child models were fetched — and code indexes into it. Without a loaded collection there is no element to return, so it is reported as out of bounds.","triggerScenarios":"`parent.children[index]` on a `HasMany` field in `Unloaded` state, i.e. the query did not include `.with(children::Entity)` or the relation was never assigned.","commonSituations":"Accessing relation collections on models loaded without eager loading; models built in memory (tests, fixtures) where relations were never populated; assuming relations auto-load lazily.","solutions":["Eager-load the relation: add `.with(children::Entity)` to the loader query before indexing.","Match on `HasMany::Loaded(items)` vs `Unloaded` and handle the unloaded case.","Iterate only when loaded, or fall back to a separate query fetching children by foreign key.","Update the code path that was supposed to populate the relation."],"exampleFix":"// before\nlet user = user::Entity::load().filter_by_id(id).one(db)?.unwrap();\nlet first = &user.posts[0]; // panics: posts Unloaded\n// after\nlet user = user::Entity::load().filter_by_id(id).with(post::Entity).one(db)?.unwrap();\nlet first = user.posts.get(0);","handlingStrategy":"validation","validationCode":"// Guard: only index when the HasMany is Loaded\nlet items = match &user.posts {\n    HasMany::Loaded(v) => Some(v.as_slice()),\n    HasMany::Unloaded => None,\n};","typeGuard":"fn as_models<'a, E: EntityTrait>(h: &'a HasMany<E>) -> Option<&'a [E::ModelEx]> {\n    match h { HasMany::Loaded(v) => Some(v), HasMany::Unloaded => None }\n}","tryCatchPattern":"// Avoid catching the panic; check load state first\nif let Some(posts) = as_models(&user.posts) {\n    if let Some(first) = posts.get(0) { /* use first */ }\n}","preventionTips":["Eager-load with .with(child::Entity) whenever you will access relation collections","Never assume lazy loading — relations default to Unloaded","Use .get(index) on the loaded slice to also guard against empty collections"],"tags":["index-out-of-bounds","panic","relation","has-many","eager-loading"],"backgroundTag":"index-out-of-bounds","analyzedSha":"e29bcd1b417c41a553b386fe94511d7c64a1c8ec","analyzedAt":"2026-09-10T11:31:52.468Z","contentChangedAt":"2026-09-10T11:31:52.468Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}