FuelLabs/fuels-ts · error · FuelError

INVALID_TTL

INVALID_TTL

Error message

Invalid TTL: ${this.ttl}. Use a value greater than zero.

What it means

Thrown by the ResourceCache constructor when ttl is not a positive finite number. The cache evicts entries based on currentTime - timestamp < ttl, so a non-positive or non-numeric ttl would make every entry either instantly expired or never compared correctly. The guard enforces typeof ttl === 'number' && ttl > 0.

Source

Thrown at packages/account/src/providers/resource-cache.ts:27

} from './transaction-request';

type ResourcesOwnersMap = Map<string, { utxos: Set<string>; messages: Set<string> }>;

interface TransactionResourcesCache {
  owners: ResourcesOwnersMap;
  timestamp: number;
}

const cache = new Map<string, TransactionResourcesCache>();

export class ResourceCache {
  readonly ttl: number;

  constructor(ttl: number) {
    this.ttl = ttl; // TTL in milliseconds

    if (typeof ttl !== 'number' || this.ttl <= 0) {
      throw new FuelError(
        ErrorCode.INVALID_TTL,
        `Invalid TTL: ${this.ttl}. Use a value greater than zero.`
      );
    }
  }

  // Add resources to the cache
  set(transactionId: string, inputs: TransactionRequestInput[]): void {
    const transactionResourceCache = this.setupResourcesCache(inputs);
    cache.set(transactionId, transactionResourceCache);
  }

  unset(transactionId: string): void {
    cache.delete(transactionId);
  }

  getActiveData(owner: string) {
    const activeData: { utxos: string[]; messages: string[] } = { utxos: [], messages: [] };

View on GitHub (pinned to b3f37c91ac)

Solutions

  1. Pass a positive integer number of milliseconds (e.g. 5000 for 5s).
  2. To disable caching, do not construct ResourceCache / pass resourceCacheTTL as undefined to the Provider.
  3. Validate env-derived TTL: const ttl = Number(process.env.TTL); if (!Number.isFinite(ttl) || ttl <= 0) skip cache.
  4. Document units (milliseconds) at the call site to avoid seconds/ms confusion.

Example fix

// before
const cache = new ResourceCache(0); // intent: disable
// or
const cache = new ResourceCache(Number(process.env.CACHE_TTL)); // NaN when unset

// after — pass a positive ttl, or disable by omitting the cache
const ttl = Number(process.env.CACHE_TTL);
const cache = Number.isFinite(ttl) && ttl > 0 ? new ResourceCache(ttl) : undefined;

// Provider usage: omit resourceCacheTTL to disable
const provider = new Provider(url, { resourceCacheTTL: undefined });
Defensive patterns

Strategy: validation

Validate before calling

function makeResourceCache(ttlFromEnv?: string) {
  const ttl = Number(ttlFromEnv);
  if (!Number.isFinite(ttl) || ttl <= 0) return undefined; // disables cache
  return new ResourceCache(ttl);
}

Type guard

function isPositiveTtl(ttl: unknown): ttl is number {
  return typeof ttl === 'number' && Number.isFinite(ttl) && ttl > 0;
}

Try / catch

import { FuelError, ErrorCode } from '@fuel-ts/errors';
let cache;
try {
  cache = new ResourceCache(ttl);
} catch (e) {
  if (e instanceof FuelError && e.code === ErrorCode.INVALID_TTL) {
    cache = undefined; // disable caching rather than crash
  } else throw e;
}

Prevention

When it happens

Trigger: Constructing new ResourceCache(ttl) with ttl <= 0, NaN, undefined (passed through as NaN), or a non-number; passing a config value parsed from env as a string; defaulting to 0 to 'disable' caching (the API does not support 0 as disable).

Common situations: Env var RESOURCE_CACHE_TTL set to '0' or empty and Number()-coerced to 0/NaN; passing milliseconds vs seconds confusion leading to a tiny value rounded to 0; intent to disable caching by passing 0 (use undefined instead — Provider sets cache = undefined when resourceCacheTTL is not provided).

Related errors


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