{"record":{"id":"4352c1db7ad50f15","repo":"nodejs/node","slug":"expected-key-to-be-object-got-typeof-key","errorCode":null,"errorMessage":"expected key to be object, got ${typeof key}","messagePattern":"expected key to be object, got (.+?)","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"deps/undici/src/lib/cache/memory-cache-store.js","lineNumber":207,"sourceCode":"          }\n\n          // Reset the event flag after eviction\n          if (store.#size < store.#maxSize && store.#count < store.#maxCount) {\n            store.#hasEmittedMaxSizeEvent = false\n          }\n        }\n\n        callback(null)\n      }\n    })\n  }\n\n  /**\n   * @param {CacheKey} key\n   */\n  delete (key) {\n    if (typeof key !== 'object') {\n      throw new TypeError(`expected key to be object, got ${typeof key}`)\n    }\n\n    const topLevelKey = `${key.origin}:${key.path}`\n\n    for (const entry of this.#entries.get(topLevelKey) ?? []) {\n      this.#size -= entry.size\n      this.#count -= 1\n    }\n    this.#entries.delete(topLevelKey)\n  }\n}\n\nfunction findEntry (key, entries, now) {\n  for (let i = 0; i < entries.length; i++) {\n    const entry = entries[i]\n    if (\n      entry.deleteAt > now &&\n      entry.method === key.method &&","sourceCodeStart":189,"sourceCodeEnd":225,"githubUrl":"https://github.com/nodejs/node/blob/1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e/deps/undici/src/lib/cache/memory-cache-store.js#L189-L225","documentation":"Thrown by MemoryCacheStore.delete() when the supplied key is not an object. The interceptor's cache key is a structured object carrying at least origin and path (delete composes `${key.origin}:${key.path}` as the top-level lookup), so a primitive cannot be used. It is a TypeError because the contract of the store API was violated, not a network/runtime failure.","triggerScenarios":"Calling memoryCacheStore.delete('http://x/y') with a string, a number, null, undefined, or a boolean instead of an object like { origin, method, path }. Reproduces in the cache-interceptor internals if a custom store wrapper forwards the wrong shape.","commonSituations":"Custom CacheStore implementations that pass the request URL string directly; migrating from a string-keyed cache; refactoring that drops the key object; tests that call delete() with a primitive for convenience.","solutions":["Pass an object containing at least origin and path, e.g. delete({ origin: 'https://example.com', method: 'GET', path: '/users' }).","If you hold a Request/URL, build the key from url.origin + url.pathname + url.search before calling delete.","If integrating with the CacheInterceptor, never call store methods yourself — let the interceptor build the key.","Add a typeof key === 'object' guard in your wrapper to fail loudly before reaching the store."],"exampleFix":"// before\nstore.delete('https://example.com/users')\n// after\nstore.delete({\n  origin: 'https://example.com',\n  method: 'GET',\n  path: '/users'\n})","handlingStrategy":"type-guard","validationCode":"if (key == null || typeof key !== 'object' || typeof key.origin !== 'string' || typeof key.path !== 'string') {\n  throw new TypeError('cache key must be an object with origin and path strings')\n}\nstore.delete(key)","typeGuard":"function isCacheKey(k) {\n  return k != null && typeof k === 'object'\n    && typeof k.origin === 'string'\n    && typeof k.path === 'string'\n    && typeof k.method === 'string'\n}","tryCatchPattern":"try {\n  store.delete(key)\n} catch (err) {\n  if (err instanceof TypeError && /expected key to be object/.test(err.message)) {\n    log.warn('skipping cache delete: malformed key', key)\n  } else {\n    throw err\n  }\n}","preventionTips":["Never call store.delete/create/read directly — go through the CacheInterceptor.","Centralize CacheKey construction in one helper that always returns an object.","Add a TypeScript type annotation matching the CacheKey interface.","Unit-test your cache wrapper with string and null inputs."],"tags":["cache","validation","type-error","undici"],"backgroundTag":null,"analyzedSha":"1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e","analyzedAt":"2026-08-13T00:53:24.642Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}