FuelLabs/fuels-ts · error · FuelError

ABI_TYPES_AND_VALUES_MISMATCH

ABI_TYPES_AND_VALUES_MISMATCH

Error message

Invalid number of arguments. Expected a minimum of ${mandatoryInputLength} arguments, received ${values.length}

What it means

Thrown by `FunctionFragment.encodeArguments` (FuelError code `ABI_TYPES_AND_VALUES_MISMATCH`) when the number of values passed is less than the count of non-optional inputs declared in the ABI. Only `Option<T>` inputs are treated as optional and may be omitted; every other input is mandatory.

Source

Thrown at packages/abi-coder/src/FunctionFragment.ts:62

  private static getSignature(abi: JsonAbiOld, fn: JsonAbiFunction): string {
    const inputsSignatures = fn.inputs.map((input) =>
      new ResolvedAbiType(abi, input).getSignature()
    );
    return `${fn.name}(${inputsSignatures.join(',')})`;
  }

  private static getFunctionSelector(functionSignature: string) {
    const hashedFunctionSignature = sha256(bufferFromString(functionSignature, 'utf-8'));
    // get first 4 bytes of signature + 0x prefix. then left-pad it to 8 bytes using toHex(8)
    return bn(hashedFunctionSignature.slice(0, 10)).toHex(8);
  }

  encodeArguments(values: InputValue[]): Uint8Array {
    const inputs = getFunctionInputs({ jsonAbi: this.jsonAbiOld, inputs: this.jsonFnOld.inputs });
    const mandatoryInputLength = inputs.filter((i) => !i.isOptional).length;
    if (values.length < mandatoryInputLength) {
      throw new FuelError(
        ErrorCode.ABI_TYPES_AND_VALUES_MISMATCH,
        `Invalid number of arguments. Expected a minimum of ${mandatoryInputLength} arguments, received ${values.length}`
      );
    }

    const coders = this.jsonFnOld.inputs.map((t) =>
      AbiCoder.getCoder(this.jsonAbiOld, t, {
        encoding: this.encoding,
      })
    );

    const argumentValues = padValuesWithUndefined(values, this.jsonFn.inputs);
    return new TupleCoder(coders).encode(argumentValues);
  }

  decodeArguments(data: BytesLike) {
    const bytes = arrayify(data);
    const nonVoidInputs = findNonVoidInputs(this.jsonAbiOld, this.jsonFnOld.inputs);

View on GitHub (pinned to b3f37c91ac)

Solutions

  1. Pass an argument for every non-optional input; the error message states the exact `mandatoryInputLength` expected vs. received.
  2. Re-run `fuels typegen` so generated TS types force the correct arity at compile time.
  3. If an input was newly made optional in Sway, recompile the contract and regenerate bindings so it is treated as optional.
  4. For dynamic call sites, filter `functionFragment.jsonFnOld.inputs` and ensure your values array covers every non-optional input.

Example fix

// before — ABI: function foo(u64, u64)
await contract.functions.foo(1).call();
// after
await contract.functions.foo(1, 2).call();
Defensive patterns

Strategy: validation

Validate before calling

// before calling, ensure you cover every non-optional input
const mandatory = functionFragment.jsonFnOld.inputs.filter(
  (i) => !getFunctionInputs({ jsonAbi: functionFragment.jsonAbiOld, inputs: [i] })[0].isOptional
);
if (values.length < mandatory.length) {
  throw new Error(`Missing ${mandatory.length - values.length} required argument(s)`);
}

Try / catch

try {
  await contract.functions.foo(...args).call();
} catch (e) {
  if (e.code === 'ABI_TYPES_AND_VALUES_MISMATCH') {
    console.error('Argument mismatch for foo. Expected:', e.message);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling `contract.functions.foo(a)` when `foo` declares two non-optional inputs; omitting a required argument because TypeScript types were bypassed (`as any`); calling `.encodeArguments([...])` directly with a short array.

Common situations: ABI regenerated with new required inputs but call sites not updated; bypassing generated types; dynamically building argument arrays from a partial data source.

Related errors


AI-assisted analysis of FuelLabs/fuels-ts@b3f37c91ac (2026-08-12). Data as JSON: /api/errors/a11a1a8e2b717fe3. Report an issue: GitHub.