SeaQL/sea-orm · warning

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

Error message

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

What it means

HasOne<E>::unwrap() was called on a Loaded(None) value: the relation was loaded and the query succeeded, but no related row matched (dangling foreign key or deleted target). unwrap() intentionally panics on an absent related row since 'none found' is a normal result callers should handle with into_option()/take() or explicit matching.

Source

Thrown at 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)

Solutions

  1. Handle the None case: if let Some(p) = loaded relation value
  2. Create the missing related row before unwrap, e.g. insert the profile
  3. Use try_unwrap() to handle missing relations gracefully

Example fix

// before
let profile = user.profile.unwrap();
// after
let profile = match user.profile.try_unwrap()? { Some(p) => p, None => create_default_profile(user).await? };
Defensive patterns

Strategy: fallback

Validate before calling

if matches!(user.profile, HasOne::Loaded(None)) { /* create or default */ }

Type guard

fn has_profile<E>(h: &HasOne<Option<E>>) -> bool { matches!(h, HasOne::Loaded(Some(_))) }

Try / catch

let profile = user.profile.try_unwrap()?.unwrap_or_else(default_profile);

Prevention

When it happens

Trigger: Calling .unwrap() on a loaded optional has_one whose related row does not exist — user has no profile row yet.

Common situations: One-to-one relations that are optional by schema; the child row was deleted; insert order mistakes where the child row was never created.

Related errors


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