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
- Validate owner != spender before calling new and return a proper error instead of letting it panic.
- In UIs, prevent selecting the owner's own account as spender (filter it out).
- 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
- Validate owner != spender at the API/UI boundary before constructing the pair.
- In approval UIs, exclude the owner's own account from the spender selection list.
- Unit-test the guard so the panic path is never reachable in production.
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
- Cache size must be greater than zero
- invalid block export configuration: {message}
- Returned AccountInfo should have code: Some(...) and so code
- Returned AccountInfo should have code: Some(...) and so code
- Invalid address value: {s}
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/cc8e11d24bcc50e7.
Report an issue: GitHub.