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.

Solutions

  1. Dedupe policies by type before encoding (keep the last/merged value per type).
  2. Inspect the full policies array rather than trusting the 'MaxFee' label in the message, since the message is hardcoded.
  3. 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

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.

Appendix: 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)