SeaQL/sea-orm · error

called `HasOne::unwrap()` on a `Loaded(None)` value

Error message

called `HasOne::unwrap()` on a `Loaded(None)` value

What it means

unwrap() was called on a Loaded(None) HasOne: the relation was eagerly loaded and the query ran, but no related row exists in the database (the FK points nowhere or the target was deleted). unwrap() deliberately refuses to invent a value for an absent related row, so it panics; callers must treat 'no related row' as a normal, expected outcome.

Solutions

  1. Treat a missing related row as valid: use into_option() or take() and handle the None case.
  2. If the row is required, validate the foreign key / restore the missing target row rather than unwrapping blindly.
  3. Use try_as_ref-style or match-based access so a dangling FK does not crash the request.
Defensive patterns

Strategy: fallback

When it happens

Trigger: Thrown at sea-orm-sync/src/entity/compound/has_one.rs:78 when the library encounters an invalid state.

Common situations: See trigger scenarios.


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

Appendix: source

Thrown at sea-orm-sync/src/entity/compound/has_one.rs:78

        match self {
            Self::Loaded(Some(model)) => Some(*model),
            Self::Unloaded | Self::Loaded(None) => None,
        }
    }

    /// Take ownership of the contained Model, if loaded, leaving `Unloaded` in place.
    pub fn take(&mut self) -> Option<E::ModelEx> {
        std::mem::take(self).into_option()
    }

    /// # Panics
    ///
    /// Panics if called on an `Unloaded` or `Loaded(None)` value.
    pub fn unwrap(self) -> E::ModelEx {
        match self {
            Self::Loaded(Some(model)) => *model,
            Self::Unloaded => panic!("called `HasOne::unwrap()` on an `Unloaded` value"),
            Self::Loaded(None) => panic!("called `HasOne::unwrap()` on a `Loaded(None)` value"),
        }
    }
}

impl<E> HasOne<E>
where
    E: EntityTrait,
    E::ActiveModelEx: From<E::ModelEx>,
{
    pub fn into_active_model(self) -> ActiveHasOne<E> {
        match self {
            Self::Loaded(Some(model)) => ActiveHasOne::set(Some(*model)),
            Self::Unloaded | Self::Loaded(None) => ActiveHasOne::NotSet,
        }
    }
}

impl<E: EntityTrait> From<HasOne<E>> for Option<Box<E::ModelEx>> {

View on GitHub (pinned to e29bcd1b41)