vuejs/vue · error · Error

renderer cache must implement at least get & set.

Error message

renderer cache must implement at least get & set.

What it means

Thrown by the RenderContext constructor when the cache option is truthy but is missing either a get or a set method. The renderer's component caching mechanism calls cache.get and cache.set during renderComponentWithCache, so both must exist. A partial cache object (e.g. only has get, or is a plain object) is rejected because silent cache misses would degrade SSR performance without warning.

Solutions

  1. Ensure the cache object implements both get(key, cb?) and set(key, val) as methods.
  2. Wrap external caches (Redis, LRU) in an adapter: { get: (k, cb) => redis.get(k, cb), set: (k, v) => redis.set(k, v) }.
  3. Pass undefined/null instead of a partial object if caching is not needed.
  4. Use lru-cache with a compatible adapter or the built-in pattern from Vue SSR docs.

Example fix

// before — raw object with no get/set
createRenderer({ cache: { ttl: 600 } })

// after — proper cache adapter
const LRU = require('lru-cache')
const lru = new LRU({ max: 1000, maxAge: 1000 * 60 * 15 })
createRenderer({
  cache: {
    get: (key, cb) => cb(lru.get(key)),
    set: (key, val) => lru.set(key, val),
    has: (key, cb) => cb(lru.has(key))
  }
})
Defensive patterns

Strategy: type-guard

Validate before calling

function isValidCache(cache: unknown): boolean {
  if (!cache) return true // no cache is valid
  return typeof (cache as any).get === 'function' && typeof (cache as any).set === 'function'
}

if (!isValidCache(options.cache)) {
  throw new Error('cache must have get and set methods')
}
createRenderer(options)

Type guard

type RenderCache = { get: (key: string, cb?: Function) => any; set: (key: string, val: string) => void; has?: (key: string, cb?: Function) => any }

function isRenderCache(v: unknown): v is RenderCache {
  if (typeof v !== 'object' || v === null) return false
  const c = v as Record<string, unknown>
  return typeof c.get === 'function' && typeof c.set === 'function'
}

Try / catch

// Thrown in RenderContext constructor, called synchronously inside render().
// Wrap the render call:
try {
  renderer.renderToString(app, cb)
} catch (e) {
  if (e.message.includes('renderer cache must implement')) {
    console.error('Fix cache adapter or remove the cache option')
  }
  throw e
}

Prevention

When it happens

Trigger: Calling createRenderer({ cache: someObject }) where someObject is truthy but lacks a get or set function. Typical with a half-implemented cache adapter, a plain Map passed wrongly (Map has get/set but they are prototype methods — still works), or a config object that was meant to configure a cache but was passed as the cache itself.

Common situations: Passing a Redis client object directly instead of wrapping it in a { get, set } adapter. Passing a configuration object { ttl: 1000 } by mistake. Using a stale cache API from an older version that had different method names.

Related errors


AI-assisted analysis of vuejs/vue@9e88707940 (2026-08-11). Data as JSON: /api/errors/e2a802c89de2e122. Report an issue: GitHub.

Appendix: source

Thrown at packages/server-renderer/src/render-context.ts:62

  get?: (key: string, cb: Function) => void
  has?: (key: string, cb: Function) => void

  constructor(options: Record<string, any>) {
    this.userContext = options.userContext
    this.activeInstance = options.activeInstance
    this.renderStates = []

    this.write = options.write
    this.done = options.done
    this.renderNode = options.renderNode

    this.isUnaryTag = options.isUnaryTag
    this.modules = options.modules
    this.directives = options.directives

    const cache = options.cache
    if (cache && (!cache.get || !cache.set)) {
      throw new Error('renderer cache must implement at least get & set.')
    }
    this.cache = cache
    this.get = cache && normalizeAsync(cache, 'get')
    this.has = cache && normalizeAsync(cache, 'has')

    this.next = this.next.bind(this)
  }

  next() {
    // eslint-disable-next-line
    while (true) {
      const lastState = this.renderStates[this.renderStates.length - 1]
      if (isUndef(lastState)) {
        return this.done()
      }
      /* eslint-disable no-case-declarations */
      switch (lastState.type) {
        case 'Element':

View on GitHub (pinned to 9e88707940)