linera-io/linera-protocol · error · ExecutionError
UnauthenticatedClaimOwner
UnauthenticatedClaimOwner
Error message
ExecutionError::UnauthenticatedClaimOwner
What it means
Claim withdraws tokens from an owner's account (possibly on another chain) and forwards them to a recipient. The claimed source owner must be authenticated: either the transaction signer equals source, or the calling application's ID equals source (applications may claim their own funds). This error means neither held, so the claim was rejected before any funds moved or any Withdraw message was created.
Source
Thrown at linera-execution/src/system.rs:769
ensure!(
amount > Amount::ZERO,
ExecutionError::IncorrectTransferAmount
);
self.debit(&source, amount).await?;
self.credit_or_send_message(source, recipient, amount).await
}
/// Claims `amount` from `source`'s account on `target_id` and transfers it to `recipient`.
pub async fn claim(
&mut self,
authenticated_owner: Option<AccountOwner>,
authenticated_application_id: Option<ApplicationId>,
source: AccountOwner,
target_id: ChainId,
recipient: Account,
amount: Amount,
) -> Result<Option<OutgoingMessage>, ExecutionError> {
ensure!(
authenticated_owner == Some(source)
|| authenticated_application_id.map(AccountOwner::from) == Some(source),
ExecutionError::UnauthenticatedClaimOwner
);
ensure!(amount > Amount::ZERO, ExecutionError::IncorrectClaimAmount);
let current_chain_id = self.context().extra().chain_id();
if target_id == current_chain_id {
// Handle same-chain claim locally by processing the withdraw operation directly
self.debit(&source, amount).await?;
self.credit_or_send_message(source, recipient, amount).await
} else {
// Handle cross-chain claim with Withdraw message
let message = SystemMessage::Withdraw {
amount,
owner: source,
recipient,
};View on GitHub (pinned to 6c226ddcb3)
Solutions
- Sign the claim operation with the key of the owner whose funds are being claimed.
- If an application performs the claim, set source to the application's own account (its ApplicationId mapped to AccountOwner) rather than a user account.
- Verify the owner field in the Claim matches the authenticated signer of the block before submitting.
Example fix
// before: claim submitted with neither signer nor app matching the claimed owner
let op = SystemOperation::Claim { owner, target_id, recipient, amount };
client.submit(op).await?; // UnauthenticatedClaimOwner
// after: sign the block with the claimed owner's key first
let block = client.prepare_block(op).sign(&owner_key).send().await?; Defensive patterns
Strategy: validation
Validate before calling
// The claimed owner must be authenticated before submitting a Claim
ensure!(
authenticated_owner == Some(source)
|| application_account == Some(source),
"claim must be signed by (or originate from) the claimed owner"
); Type guard
fn is_unauthenticated_claim(e: &ExecutionError) -> bool {
matches!(e, ExecutionError::UnauthenticatedClaimOwner)
} Try / catch
match result {
Err(ExecutionError::UnauthenticatedClaimOwner) => {
// Signer/app does not match the claimed owner: re-sign as that
// owner or claim from the application's own account.
}
Err(e) => return Err(e.into()),
Ok(value) => { /* ... */ }
} Prevention
- Sign claim operations with the key of the owner whose funds are claimed.
- In applications, restrict runtime.claim to the app's own account unless the user signed the operation.
- Distinguish the owner (funds being claimed) from the recipient when building the Claim.
When it happens
Trigger: Submitting SystemOperation::Claim { owner, target_id, recipient, amount } where owner is neither the block's authenticated signer nor the calling application; an application calling runtime.claim(...) over a user's remote account without that user's signature on the operation.
Common situations: Reclaiming funds from another chain with the wrong wallet or key; applications attempting to sweep user balances without user authorization; mixing up the owner and recipient parameters when constructing the Claim operation.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Invalid owner address
- Incoming message bundle in block proposed to {chain_id} has
- Cannot vote for block proposal of chain {chain_id} because {
- InvalidOwner
- InvalidSigner
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/b1c8edda363e3b53.
Report an issue: GitHub.