FuelLabs/fuels-ts · error · FuelError

DECODE_ERROR

DECODE_ERROR

Error message

Invalid vec data size.

What it means

Thrown by VecCoder.decode() under two conditions: (1) the vector has no nested Option fields and the data buffer is shorter than WORD_SIZE (8 bytes), too small to read the u64 element-count prefix; or (2) the data buffer exceeds MAX_BYTES (2^32 - 1), a safety guard against unreasonably large or potentially malicious payloads. Both indicate the data cannot be safely decoded.

Source

Thrown at packages/abi-coder/src/encoding/coders/VecCoder.ts:50

        `Expected array value, or a Uint8Array. You can use arrayify to convert a value to a Uint8Array.`
      );
    }

    const lengthCoder = new BigNumberCoder('u64');

    if (isUint8Array(value)) {
      return new Uint8Array([...lengthCoder.encode(value.length), ...value]);
    }

    const bytes = value.map((v) => this.coder.encode(v));
    const lengthBytes = lengthCoder.encode(value.length);

    return new Uint8Array([...lengthBytes, ...concatBytes(bytes)]);
  }

  decode(data: Uint8Array, offset: number): [DecodedValueOf<TCoder>, number] {
    if ((!this.#hasNestedOption && data.length < this.encodedLength) || data.length > MAX_BYTES) {
      throw new FuelError(ErrorCode.DECODE_ERROR, `Invalid vec data size.`);
    }

    const offsetAndLength = offset + WORD_SIZE;
    const lengthBytes = data.slice(offset, offsetAndLength);
    const length = bn(new BigNumberCoder('u64').decode(lengthBytes, 0)[0]).toNumber();
    const dataLength = length * this.coder.encodedLength;
    const dataBytes = data.slice(offsetAndLength, offsetAndLength + dataLength);

    if (!this.#hasNestedOption && dataBytes.length !== dataLength) {
      throw new FuelError(ErrorCode.DECODE_ERROR, `Invalid vec byte data size.`);
    }

    let newOffset = offsetAndLength;
    const chunks = [];
    for (let i = 0; i < length; i++) {
      const [decoded, optionOffset] = this.coder.decode(data, newOffset);
      chunks.push(decoded);
      newOffset = optionOffset;

View on GitHub (pinned to b3f37c91ac)

Solutions

  1. Verify data.length is between 8 and MAX_BYTES before calling decode().
  2. Validate data size from untrusted sources before attempting to decode.
  3. Check for truncation or corruption in the data pipeline.

Example fix

// before
vecCoder.decode(new Uint8Array([0, 1, 2]), 0); // throws DECODE_ERROR

// after
const MAX_BYTES = 2 ** 32 - 1;
if (data.length < 8 || data.length > MAX_BYTES) {
  throw new Error('Invalid vec data size');
}
vecCoder.decode(data, 0);
Defensive patterns

Strategy: validation

Validate before calling

const MAX_BYTES = 2 ** 32 - 1;
function canDecodeVec(data: Uint8Array): boolean {
  return data.length >= 8 && data.length <= MAX_BYTES;
}

Try / catch

try {
  const [decoded, offset] = vecCoder.decode(data, 0);
} catch (e) {
  if (e instanceof FuelError && e.code === ErrorCode.DECODE_ERROR) {
    // data too short or exceeds MAX_BYTES; reject the payload
  }
  throw e;
}

Prevention

When it happens

Trigger: Passing fewer than 8 bytes to decode(), failing the minimum-size check. Receiving a corrupted or adversarial payload whose total size exceeds MAX_BYTES (approximately 4 GB). Both conditions indicate data that is either truncated or dangerously oversized.

Common situations: Truncated RPC data failing the minimum-size check. Corrupted or adversarial data triggering the MAX_BYTES guard. Memory exhaustion scenarios with very large vectors from untrusted sources.

Related errors


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