{"record":{"id":"e2a802c89de2e122","repo":"vuejs/vue","slug":"renderer-cache-must-implement-at-least-get-set","errorCode":null,"errorMessage":"renderer cache must implement at least get & set.","messagePattern":"renderer cache must implement at least get & set\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/server-renderer/src/render-context.ts","lineNumber":62,"sourceCode":"  get?: (key: string, cb: Function) => void\n  has?: (key: string, cb: Function) => void\n\n  constructor(options: Record<string, any>) {\n    this.userContext = options.userContext\n    this.activeInstance = options.activeInstance\n    this.renderStates = []\n\n    this.write = options.write\n    this.done = options.done\n    this.renderNode = options.renderNode\n\n    this.isUnaryTag = options.isUnaryTag\n    this.modules = options.modules\n    this.directives = options.directives\n\n    const cache = options.cache\n    if (cache && (!cache.get || !cache.set)) {\n      throw new Error('renderer cache must implement at least get & set.')\n    }\n    this.cache = cache\n    this.get = cache && normalizeAsync(cache, 'get')\n    this.has = cache && normalizeAsync(cache, 'has')\n\n    this.next = this.next.bind(this)\n  }\n\n  next() {\n    // eslint-disable-next-line\n    while (true) {\n      const lastState = this.renderStates[this.renderStates.length - 1]\n      if (isUndef(lastState)) {\n        return this.done()\n      }\n      /* eslint-disable no-case-declarations */\n      switch (lastState.type) {\n        case 'Element':","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/vuejs/vue/blob/9e88707940088cb1f4cd7dd210c9168a50dc347c/packages/server-renderer/src/render-context.ts#L44-L80","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure the cache object implements both get(key, cb?) and set(key, val) as methods.","Wrap external caches (Redis, LRU) in an adapter: { get: (k, cb) => redis.get(k, cb), set: (k, v) => redis.set(k, v) }.","Pass undefined/null instead of a partial object if caching is not needed.","Use lru-cache with a compatible adapter or the built-in pattern from Vue SSR docs."],"exampleFix":"// before — raw object with no get/set\ncreateRenderer({ cache: { ttl: 600 } })\n\n// after — proper cache adapter\nconst LRU = require('lru-cache')\nconst lru = new LRU({ max: 1000, maxAge: 1000 * 60 * 15 })\ncreateRenderer({\n  cache: {\n    get: (key, cb) => cb(lru.get(key)),\n    set: (key, val) => lru.set(key, val),\n    has: (key, cb) => cb(lru.has(key))\n  }\n})","handlingStrategy":"type-guard","validationCode":"function isValidCache(cache: unknown): boolean {\n  if (!cache) return true // no cache is valid\n  return typeof (cache as any).get === 'function' && typeof (cache as any).set === 'function'\n}\n\nif (!isValidCache(options.cache)) {\n  throw new Error('cache must have get and set methods')\n}\ncreateRenderer(options)","typeGuard":"type RenderCache = { get: (key: string, cb?: Function) => any; set: (key: string, val: string) => void; has?: (key: string, cb?: Function) => any }\n\nfunction isRenderCache(v: unknown): v is RenderCache {\n  if (typeof v !== 'object' || v === null) return false\n  const c = v as Record<string, unknown>\n  return typeof c.get === 'function' && typeof c.set === 'function'\n}","tryCatchPattern":"// Thrown in RenderContext constructor, called synchronously inside render().\n// Wrap the render call:\ntry {\n  renderer.renderToString(app, cb)\n} catch (e) {\n  if (e.message.includes('renderer cache must implement')) {\n    console.error('Fix cache adapter or remove the cache option')\n  }\n  throw e\n}","preventionTips":["Wrap external stores (Redis, Memcached) in an explicit { get, set, has } adapter before passing.","Do not pass configuration objects (e.g. { ttl }) as the cache; pass the adapter instance.","Unit-test the cache adapter to confirm get/set exist and return expected shapes.","If caching is optional, default the cache option to undefined, not to a partial object."],"tags":["ssr","cache","renderer","validation","adapter"],"analyzedSha":"9e88707940088cb1f4cd7dd210c9168a50dc347c","analyzedAt":"2026-08-11T22:22:01.295Z","schemaVersion":2},"datasetVersion":"2026-08-12T04:17:13.124Z"}