SeaQL/sea-orm · error

item is a full model

Error message

item is a full model

What it means

`establish_links` unwraps `leftover.get_primary_key_value()` with `.expect("item is a full model")` (src/entity/active_model.rs:1296). The precondition is that every leftover junction row is a fully-loaded Model (all columns present, including primary key). Panicking means a model without a complete primary key value was treated as a full model — normally impossible when models come straight from a DB query.

Source

Thrown at src/entity/active_model.rs:1296

        let via_key = get_key_from_active_model(&right.from_col, &via)?;
        if !leftover.iter().any(|t| t.1 == via_key) {
            // if not already exist, save for insert
            via_models.push(via);
        }
        if delete_leftover {
            all_keys.insert(via_key);
        }
    }

    if delete_leftover {
        let mut to_delete = Vec::new();
        let mut to_delete_am = Vec::new();
        for (leftover, key) in leftover {
            if !all_keys.contains(&key) {
                to_delete.push(
                    leftover
                        .get_primary_key_value()
                        .expect("item is a full model"),
                );
                to_delete_am.push(leftover);
            }
        }
        if !to_delete.is_empty() {
            // run before_delete hooks
            for am in to_delete_am.clone() {
                am.before_delete(db).await?;
            }
            if db.support_returning() {
                let deleted = J::delete_many()
                    .filter_by_value_tuples(&to_delete, db.get_database_backend())
                    .exec_with_returning(db)
                    .await?;
                // run after_delete hooks with the returned value if possible
                for am in deleted {
                    let am = am.into_active_model();
                    let _ = am.after_delete(db).await?;

View on GitHub (pinned to e29bcd1b41)

Solutions

  1. Ensure the junction models passed through establish_links come from a database select so primary keys are populated.
  2. Do not construct Models by hand for link sync; use ActiveModels.
  3. Check that the junction entity's primary key is correctly declared (composite, auto_increment=false).
  4. If using IntoActiveModel conversions, verify all PK columns were Set before conversion.

Example fix

// before (hand-built model missing PK)
let j = junction::Model { post_id: 1, /* tag_id missing */ };
// after
let j = junction::Entity::find_by_id((post.id, tag.id)).one(db)?.unwrap();
Defensive patterns

Strategy: validation

Validate before calling

// ensure leftover models have complete primary keys before link sync
for m in leftovers {
    assert!(m.get_primary_key_value().is_some(), "junction model missing PK");
}

Type guard

fn is_full_model<M: ModelTrait>(m: &M) -> bool {
    m.get_primary_key_value().is_some()
}

Prevention

When it happens

Trigger: Calling `insert_many`/link-establishing APIs (e.g. `save` with delete_leftover semantics) where a leftover junction Model lacks primary key values — e.g. an ActiveModel converted to Model prematurely, or a Model constructed in code with None/zeroed keys.

Common situations: Programmatically constructing junction Models instead of loading them; custom ORM behavior overrides that return partially-populated models into establish_links.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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