remix-run/remix · error · TypeError

files.cache must implement the FileStorage interface

Error message

files.cache must implement the FileStorage interface

What it means

When `files.cache` is provided it must implement the FileStorage interface (objects with `get` and `set` functions). The library duck-types the option at normalization time rather than trusting the type system, because users often pass plain objects or partially-implemented caches.

Source

Thrown at packages/assets/src/lib/files/config.ts:302

        transform.extensions,
        `files.globalTransforms[${index}].extensions`,
      ),
    })
  }

  let maxRequestTransforms = files.maxRequestTransforms ?? defaultMaxRequestTransforms
  if (!Number.isInteger(maxRequestTransforms) || maxRequestTransforms < 1) {
    throw new TypeError('files.maxRequestTransforms must be a positive integer')
  }

  if (files.cache !== undefined) {
    if (
      files.cache === null ||
      typeof files.cache !== 'object' ||
      typeof files.cache.get !== 'function' ||
      typeof files.cache.set !== 'function'
    ) {
      throw new TypeError('files.cache must implement the FileStorage interface')
    }
  }

  return {
    cache: files.cache,
    extensions: normalizedExtensions,
    globalTransforms: normalizedGlobalTransforms,
    hasTransforms: normalizedTransforms.size > 0 || normalizedGlobalTransforms.length > 0,
    maxRequestTransforms,
    transforms: normalizedTransforms,
  }
}

function normalizeTransformExtensions(
  extensions: readonly string[] | undefined,
  optionPath: string,
): readonly string[] | undefined {
  if (extensions === undefined) return undefined

View on GitHub (pinned to 9696913134)

Solutions

  1. Implement both get and set methods on the cache object
  2. If wrapping an external cache, adapt its method names: `get: (k) => store.read(k), set: (k, v) => store.write(k, v)`
  3. Omit `cache` to use the default cache behavior

Example fix

// before
files: { cache: {} }
// after
files: {
  cache: {
    get(key) { return myStore[key] },
    set(key, value) { myStore[key] = value },
  },
}
Defensive patterns

Strategy: type-guard

Validate before calling

const c = options.files?.cache
if (c && (typeof c.get !== 'function' || typeof c.set !== 'function')) throw new Error('cache must implement FileStorage')

Type guard

function isFileStorage(c: unknown): c is FileStorage {
  return c != null && typeof c === 'object'
    && typeof (c as FileStorage).get === 'function'
    && typeof (c as FileStorage).set === 'function'
}

Prevention

When it happens

Trigger: Passing `files: { cache: {} }`, a Map instance, a cache module namespace without get/set, or null to resolveAssetServerOptions/normalizeFilesOptions.

Common situations: Trying to use a custom in-memory LRU or a plain object literal as cache, wrapping an existing cache library whose methods are named differently (e.g. read/write), or passing a cache that was serialized/structured-cloned so methods were lost.

Understand the failure class

Background: Invalid option value errors: "must be one of", "is not a valid", and "only allows" failures explained — this error's family across 23 libraries.

Related errors


AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27). Data as JSON: /api/errors/79b631e421283232. Report an issue: GitHub.