linera-io/linera-protocol · error

owner should be different from spender

Error message

owner should be different from spender

What it means

OwnerSpender models a token-allowance pair: the owner account that holds funds and a different spender account authorized to withdraw. OwnerSpender::new panics by design when owner == spender, because a self-allowance is meaningless in the approval model. This is a Rust panic, not a Result — it crashes the calling thread/task.

Source

Thrown at linera-base/src/identifiers.rs:212

        }
    }
}

/// A pair of owner and spender accounts for managing allowances.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Allocative)]
pub struct OwnerSpender {
    /// Account to withdraw from
    pub owner: AccountOwner,
    /// Account to do the withdrawing
    pub spender: AccountOwner,
}

impl OwnerSpender {
    /// Creates a new `OwnerSpender` pair.
    /// Panics if owner and spender are the same.
    pub fn new(owner: AccountOwner, spender: AccountOwner) -> Self {
        if owner == spender {
            panic!("owner should be different from spender");
        }
        Self { owner, spender }
    }
}

/// The unique identifier (UID) of a chain. This is currently computed as the hash value
/// of a [`ChainDescription`].
#[derive(
    Eq,
    PartialEq,
    Ord,
    PartialOrd,
    Copy,
    Clone,
    Hash,
    Serialize,
    Deserialize,
    WitLoad,

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Validate owner != spender before calling new and return a proper error instead of letting it panic.
  2. In UIs, prevent selecting the owner's own account as spender (filter it out).
  3. In approval flows, take the spender from a distinct party (e.g. the app's/service's account), never from the caller by default.

Example fix

// before
let pair = OwnerSpender::new(owner, owner); // panic!

// after
if owner == spender { return Err("spender must differ from owner"); }
let pair = OwnerSpender::new(owner, spender);
Defensive patterns

Strategy: validation

Validate before calling

fn build_pair(owner: AccountOwner, spender: AccountOwner) -> Result<OwnerSpender, String> {
    if owner == spender {
        return Err("spender must be different from owner".into());
    }
    Ok(OwnerSpender::new(owner, spender))
}

Type guard

fn distinct_accounts(owner: &AccountOwner, spender: &AccountOwner) -> bool {
    owner != spender
}

Prevention

When it happens

Trigger: Constructing OwnerSpender::new(owner, spender) with the same AccountOwner in both roles: an app defaulting spender to the caller's own account, a UI that lets a user pick themselves as spender, or deserialized data where both fields ended up identical.

Common situations: Building approval/allowance flows (approve + transfer_from style) where the caller is also the funds owner; tests constructing the pair with two copies of the same constant address; copy-paste where owner was pasted into the spender field.

Related errors


AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22). Data as JSON: /api/errors/cc8e11d24bcc50e7. Report an issue: GitHub.