FuelLabs/fuels-ts · error · FuelError
DUPLICATED_POLICY
DUPLICATED_POLICY
Error message
Duplicate policy type found: ${PolicyType.MaxFee} What it means
Thrown by validateDuplicatedPolicies() when two or more policies in the supplied array share the same PolicyType. Each policy type may appear at most once in a transaction's policy list. NOTE a message bug: the text always interpolates PolicyType.MaxFee (a constant) regardless of which type actually duplicated, so 'MaxFee' in the message may be misleading.
Source
Thrown at packages/transactions/src/coders/policy.ts:62
export type PolicyMaxFee = {
type: PolicyType.MaxFee;
data: BN;
};
export type PolicyOwner = {
type: PolicyType.Owner;
data: BN;
};
export const sortPolicies = (policies: Policy[]): Policy[] =>
policies.sort((a, b) => a.type - b.type);
function validateDuplicatedPolicies(policies: Policy[]): void {
const seenTypes = new Set<PolicyType>();
policies.forEach((policy) => {
if (seenTypes.has(policy.type)) {
throw new FuelError(
ErrorCode.DUPLICATED_POLICY,
`Duplicate policy type found: ${PolicyType.MaxFee}`
);
}
seenTypes.add(policy.type);
});
}
export function getPolicyTypesArray(policyTypes: number): number[] {
const out: number[] = [];
let m = policyTypes >>> 0;
while (m !== 0) {
const low = m & -m;
out.push(low);
m &= m - 1;
}
return out;
}View on GitHub (pinned to b3f37c91ac)
Solutions
- Dedupe policies by type before encoding (keep the last/merged value per type).
- Inspect the full policies array rather than trusting the 'MaxFee' label in the message, since the message is hardcoded.
- Build policies through a single source/map keyed by PolicyType to avoid accidental duplicates.
Example fix
// before
const policies = [
{ type: PolicyType.MaxFee, data: bn(100) },
{ type: PolicyType.MaxFee, data: bn(200) },
];
// after — dedupe by type
const byType = new Map(policies.map(p => [p.type, p]));
const deduped = [...byType.values()]; Defensive patterns
Strategy: validation
Validate before calling
import { PolicyType, type Policy } from '@fuel-ts/transactions';
function dedupePolicies(policies: Policy[]): Policy[] {
const map = new Map<PolicyType, Policy>();
for (const p of policies) map.set(p.type, p); // last wins
return [...map.values()];
}
const clean = dedupePolicies(policies); Type guard
function hasUniquePolicyTypes(policies: Policy[]): boolean {
return new Set(policies.map(p => p.type)).size === policies.length;
} Try / catch
try {
validateDuplicatedPolicies(policies);
} catch (e) {
if (e instanceof FuelError && e.code === ErrorCode.DUPLICATED_POLICY) {
// NOTE: message always says 'MaxFee'; dedupe by actual type and retry
policies = dedupePolicies(policies);
} else throw e;
} Prevention
- Build policies from a single Map keyed by PolicyType to avoid duplicates.
- Do not trust the 'MaxFee' text in the message — inspect the array directly.
- Dedupe before encoding whenever merging policy sources.
When it happens
Trigger: Passing a policies array with two entries of the same type, e.g. [{type: PolicyType.MaxFee,...},{type: PolicyType.MaxFee,...}] or two Tip policies; building policies from a map/list without deduping by type.
Common situations: Merging policy lists from multiple sources that both set MaxFee/Tip; default policy plus a user override both present; programmatically pushing a policy that already exists.
Related errors
AI-assisted analysis of FuelLabs/fuels-ts@b3f37c91ac (2026-08-12).
Data as JSON: /api/errors/dee2311524c9c567.
Report an issue: GitHub.