FuelLabs/fuel-core · error
required balances contain duplicate (asset, account) pair
Error message
required balances contain duplicate (asset, account) pair
What it means
The assembler requires the required_balances list to be unique per (asset_id, account owner) pair. It rejects the request when two entries reference the same asset for the same owner, because coin selection and change handling assume one balance requirement per pair.
Source
Thrown at crates/fuel-core/src/schema/tx/assemble_tx.rs:197
return Err(anyhow::anyhow!(
"The transaction has more inputs than allowed by the consensus"
));
}
if tx.outputs().len() > max_outputs as usize {
return Err(anyhow::anyhow!(
"The transaction has more outputs than allowed by the consensus"
));
}
if arguments.fee_index as usize >= arguments.required_balances.len() {
return Err(anyhow::anyhow!("The fee address index is out of bounds"));
}
if has_duplicates(&arguments.required_balances, |balance| {
(balance.asset_id, balance.account.owner())
}) {
return Err(anyhow::anyhow!(
"required balances contain duplicate (asset, account) pair"
));
}
let base_asset_id = *arguments.consensus_parameters.base_asset_id();
let mut signature_witness_indexes = HashMap::<Address, u16>::new();
// Exclude inputs that already are used by the transaction
let mut has_predicates = false;
let inputs = tx.inputs();
let mut unique_used_asset = HashSet::new();
let mut set_contracts = HashSet::new();
for input in inputs {
if let Some(utxo_id) = input.utxo_id() {
arguments.exclude.exclude(CoinId::Utxo(*utxo_id));
}
if let Some(nonce) = input.nonce() {View on GitHub (pinned to b9d4d170da)
Solutions
- Group and sum amounts client-side so each (assetId, owner) appears exactly once
- If the same owner needs balances for different assets, keep them as separate entries — only identical (asset, owner) pairs are rejected
- Use the same change policy for the merged entry to avoid also hitting the multiple-change-policies error
Example fix
// before
const requiredBalances = [
{ account: alice, assetId: BASE_ASSET, amount: 100 },
{ account: alice, assetId: BASE_ASSET, amount: 50 }, // duplicate pair
];
// after
const merged = new Map();
for (const b of rawBalances) {
const k = `${b.account}:${b.assetId}`;
const prev = merged.get(k) ?? { ...b, amount: 0 };
prev.amount += b.amount;
merged.set(k, prev);
}
const requiredBalances = [...merged.values()]; Defensive patterns
Strategy: validation
Validate before calling
function dedupeBalances<T extends { account: string; assetId: string; amount: bigint }>(balances: T[]): T[] {
const out = new Map<string, T>();
for (const b of balances) {
const k = `${b.account}:${b.assetId}`;
const prev = out.get(k);
if (prev) prev.amount += b.amount;
else out.set(k, { ...b });
}
return [...out.values()];
} Type guard
function hasUniquePairs(balances: { account: string; assetId: string }[]): boolean {
return new Set(balances.map((b) => `${b.account}:${b.assetId}`)).size === balances.length;
} Try / catch
catch (e) { if (/duplicate \(asset, account\) pair/.test(e.message)) { requiredBalances = dedupeBalances(requiredBalances); /* retry */ } else throw e; } Prevention
- Always build requiredBalances through a grouping map keyed by (asset, account)
- Remember only identical (asset, owner) pairs collide — different assets for the same account are fine
- Merge change policies when merging duplicate entries
When it happens
Trigger: Passing two requiredBalances entries with the same assetId and the same account owner (different amount or change policy does not matter — the (asset, owner) key collides).
Common situations: Building the balances list in a loop over transfers without grouping by (asset, account); merging two wallet configurations that both add the base asset for the fee account; adding the fee payer separately when it is already present.
Related errors
- The fee address index is out of bounds
- The asset {} has multiple change policies
- The transaction has more outputs than allowed by the consens
- fee index out of bounds
- The `message_block_height` is higher than `commit_block_heig
AI-assisted analysis of FuelLabs/fuel-core@b9d4d170da (2026-08-16).
Data as JSON: /api/errors/269e81a84424615f.
Report an issue: GitHub.