{"id":"61d1af47d12faa4e","repo":"vitest-dev/vitest","slug":"cannot-provide-key-because-it-s-not-serializa","errorCode":null,"errorMessage":"Cannot provide \"${key}\" because it's not serializable.","messagePattern":"Cannot provide \"(.+?)\" because it's not serializable\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/vitest/src/node/project.ts","lineNumber":132,"sourceCode":"          this._fetcher,\n          this.config,\n        )\n  }\n\n  // \"provide\" is a property, not a method to keep the context when destructed in the global setup,\n  // making it a method would be a breaking change, and can be done in Vitest 3 at minimum\n  /**\n   * Provide a value to the test context. This value will be available to all tests with `inject`.\n   */\n  provide = <T extends keyof ProvidedContext & string>(\n    key: T,\n    value: ProvidedContext[T],\n  ): void => {\n    try {\n      structuredClone(value)\n    }\n    catch (err) {\n      throw new Error(\n        `Cannot provide \"${key}\" because it's not serializable.`,\n        {\n          cause: err,\n        },\n      )\n    }\n    // casting `any` because the default type is `never` since `ProvidedContext` is empty\n    (this._provided as any)[key] = value\n  }\n\n  /**\n   * Get the provided context. The project context is merged with the global context.\n   */\n  getProvidedContext(): ProvidedContext {\n    if (this.isRootProject()) {\n      return this._provided\n    }\n    // globalSetup can run even if core workspace is not part of the test run","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/vitest-dev/vitest/blob/d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09/packages/vitest/src/node/project.ts#L114-L150","documentation":"Vitest's project.provide() injects values into the test context via inject(), but those values cross a process boundary (worker threads / child processes) and so must be structured-cloneable. The method (project.ts:124) runs structuredClone(value) as a guard and rethrows with this message when it fails. Non-cloneable values (functions, DOM nodes, class instances with non-cloneable internals, objects with circular refs) cannot be delivered to tests.","triggerScenarios":"Calling project.provide('myKey', value) where value is a function, a class instance holding a non-cloneable field, a cyclic object, a WeakMap/WeakSet, or any value structuredClone rejects. Also triggered indirectly via config.provide in the test config object.","commonSituations":"Passing a database connection handle, an Express app instance, a logger with open sockets, a function/callback, or a reactive store wrapper (e.g. a Vue ref's internals) into provide(). Also hits when provide receives an object that secretly contains a circular reference.","solutions":["Provide only plain serializable data: strings, numbers, booleans, arrays, plain objects, Date, RegExp, Map, Set, ArrayBuffer, TypedArrays.","If you must pass a non-serializable resource, provide a factory pattern: provide a config descriptor (URLs, options) and construct the resource inside setup() or the test instead.","Run structuredClone(value) yourself in a try/catch before calling provide to surface the exact clone failure and the offending key.","For functions, use vi.fn() only inside tests; never inject live spies across the boundary."],"exampleFix":"// before\nproject.provide('db', openDatabaseConnection())\n\n// after\nproject.provide('dbConfig', { url: 'postgres://...', pool: 10 })\n// construct the connection inside setupFiles or the test","handlingStrategy":"validation","validationCode":"function isSerializable(value) {\n  try { structuredClone(value); return true } catch { return false }\n}\n// before provide:\nif (!isSerializable(value)) throw new Error(`Refusing to provide non-serializable value for key '${key}'`)\nproject.provide(key, value)","typeGuard":"function isStructuredCloneable<T>(v: T): boolean {\n  try { structuredClone(v); return true } catch { return false }\n}","tryCatchPattern":"try {\n  project.provide(key, value)\n} catch (e) {\n  if (e instanceof Error && /not serializable/.test(e.message)) {\n    // fall back to a serializable descriptor instead of the live object\n  } else throw e\n}","preventionTips":["Only provide plain JSON-like data; construct resources inside tests/setupFiles.","Run a pre-flight structuredClone over the whole provide object in a config plugin.","Document which keys are injectable and their expected (serializable) shape."],"tags":["serialization","provide","worker","structured-clone","test-context"],"analyzedSha":"d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09","analyzedAt":"2026-08-03T20:23:56.861Z","schemaVersion":2}