{"id":10643,"library":"cls-hooked","title":"Continuation-Local Storage (Hooked)","description":"cls-hooked is a Node.js library that provides Continuation-Local Storage (CLS), a mechanism akin to thread-local storage but adapted for Node.js's asynchronous, callback-based execution model. It enables developers to maintain and implicitly pass contextual data across asynchronous operations without explicit parameter passing. The current stable version, 4.2.2, primarily targets Node.js versions `^4.7 || >=6.9 || >=7.3 || >=8.2.1`. Its key differentiator lies in its implementation, which leverages Node.js's internal, lower-level APIs: `AsyncWrap` for Node.js versions prior to v8 (an unofficial API), and the `async_hooks` API for Node.js v8.2.1 and newer (which is considered experimental). This approach distinguishes it from its predecessor, `node-continuation-local-storage`, which relied on the now-deprecated `async-listener`. The project's release cadence is largely influenced by the evolution and stability of these underlying Node.js APIs, aiming to provide a robust CLS implementation despite the experimental nature of its core dependencies.","status":"active","version":"4.2.2","language":"javascript","source_language":"en","source_url":"https://github.com/jeff-lewis/cls-hooked","tags":["javascript","threading","shared","context"],"install":[{"cmd":"npm install cls-hooked","lang":"bash","label":"npm"},{"cmd":"yarn add cls-hooked","lang":"bash","label":"yarn"},{"cmd":"pnpm add cls-hooked","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Core dependency for `AsyncWrap` implementation, a fork of `async-hook` to address engine semver issues and provide Babel support.","package":"async-hook-jl","optional":false}],"imports":[{"note":"Primary usage is CommonJS `require` due to Node.js engine targets (v4+). Direct ESM `import` may require bundler or Node.js loader configuration.","wrong":"import { createNamespace } from 'cls-hooked';","symbol":"createNamespace","correct":"const cls = require('cls-hooked');\nconst myNamespace = cls.createNamespace('my-app');"},{"note":"Used to retrieve an already created namespace. Ensure the namespace has been created via `createNamespace` first.","wrong":"import { getNamespace } from 'cls-hooked';","symbol":"getNamespace","correct":"const cls = require('cls-hooked');\nconst myNamespace = cls.getNamespace('my-app');"},{"note":"The `run` method establishes the continuation-local storage context for the duration of the provided function. All `set`/`get` operations within this callback's async flow will use this context.","symbol":"Namespace.run","correct":"const session = cls.createNamespace('my-session');\nsession.run(() => { /* contextual code here */ });"}],"quickstart":{"code":"const cls = require('cls-hooked');\nconst session = cls.createNamespace('my-app-session');\n\nconsole.log('--- Starting CLS Example ---');\n\nfunction performAsyncTask(callback) {\n  setTimeout(() => {\n    // Simulate some async operation that takes time\n    console.log(`[Async Task] Inside async operation. Context user: ${session.get('user') || 'N/A'}`);\n    callback();\n  }, 50);\n}\n\nsession.run(() => {\n  const requestId = 'req-123';\n  session.set('requestId', requestId);\n  session.set('user', 'Alice');\n  console.log(`[Main Thread] Initial context set. Request ID: ${session.get('requestId')}, User: ${session.get('user')}`);\n\n  performAsyncTask(() => {\n    // Even after an async operation, the context should persist\n    console.log(`[Callback 1] After async task. Request ID: ${session.get('requestId')}, User: ${session.get('user')}`);\n\n    // Call another function that accesses the context\n    anotherFunction();\n\n    // Now, run another context nested within\n    session.run(() => {\n      session.set('user', 'Bob'); // This should be specific to the nested run\n      console.log(`[Nested Context] Inside nested run. Request ID: ${session.get('requestId')}, User: ${session.get('user')}`);\n      performAsyncTask(() => {\n        console.log(`[Nested Callback] After nested async task. Request ID: ${session.get('requestId')}, User: ${session.get('user')}`);\n      });\n    });\n\n    console.log(`[Callback 1 Cont.] After nested context block. User: ${session.get('user')} (should be Alice again)`);\n  });\n});\n\nfunction anotherFunction() {\n  console.log(`[Another Function] Accessing context. Request ID: ${session.get('requestId')}, User: ${session.get('user')}`);\n}\n\n// Outside any 'run' block, the context should not be available\nsetTimeout(() => {\n  console.log(`[Outside Context] After all operations. Request ID: ${session.get('requestId') || 'N/A'}, User: ${session.get('user') || 'N/A'}`);\n}, 150);\n","lang":"javascript","description":"This quickstart demonstrates how to create and manage continuation-local storage contexts using `cls-hooked`'s `createNamespace`, `run`, `set`, and `get` methods, including persistence across asynchronous operations and nested contexts."},"warnings":[{"fix":"Consider upgrading to Node.js v8.2.1 or newer to utilize the `async_hooks` API. Be aware of the `async_hooks` API's experimental status.","message":"When running `cls-hooked` on Node.js versions prior to v8, the library relies on Node.js's internal `AsyncWrap` API, which is unofficial, undocumented, and not guaranteed to be stable or supported across Node.js minor versions. Use in production environments should consider this inherent risk.","severity":"gotcha","affected_versions":"<8.0.0"},{"fix":"Regularly update `cls-hooked` to its latest version to ensure compatibility with newer Node.js releases, especially those introducing changes to the `async_hooks` API.","message":"For Node.js versions v8.2.1 and above, `cls-hooked` utilizes the `async_hooks` API, which is labeled as `Experimental` by Node.js. This implies that the API may undergo breaking changes in future Node.js releases without a major version increment, potentially requiring updates to `cls-hooked` itself.","severity":"gotcha","affected_versions":">=8.2.1"},{"fix":"Ensure your `cls-hooked` version is compatible with your Node.js version. Refer to `cls-hooked` release notes and `engines` field in `package.json` for specific compatibility information.","message":"The underlying `async_hooks` API in Node.js has undergone breaking changes, for example, noted around Node.js v8.2.0, which necessitated specific updates within `cls-hooked` (e.g., v5.alpha.1 notes this). These changes in Node.js itself can break `cls-hooked` functionality if not using a compatible version.","severity":"breaking","affected_versions":">=8.0.0"},{"fix":"Upgrade to `cls-hooked` version 4.2.1 or newer to benefit from memory leak reductions and improvements.","message":"Prior to version 4.2.1, `cls-hooked` had known issues with memory leaks due to improper handling of asynchronous resources. These leaks could accumulate over time in long-running applications.","severity":"gotcha","affected_versions":"<4.2.1"}],"env_vars":null,"last_verified":"2026-04-19T00:00:00.000Z","next_check":"2026-07-18T00:00:00.000Z","problems":[{"fix":"If a namespace with the desired name might already exist, retrieve it using `const myNamespace = cls.getNamespace('my-app-session');` instead of `createNamespace`.","cause":"Attempting to create a new continuation-local storage namespace using `createNamespace(name)` with a name that is already in use.","error":"Error: A namespace called 'my-app-session' already exists. Use getNamespace(name) to retrieve a namespace."},{"fix":"Ensure that any code that needs to access or modify the CLS context is executed within a `namespace.run(function() { ... })` block, or by passing functions through `namespace.bind(func)` to propagate the context.","cause":"This error occurs when attempting to call `namespace.set()` or `namespace.get()` outside of an active CLS context, meaning the code is not running within a `namespace.run()` block or a function bound by `namespace.bind()`.","error":"TypeError: Cannot read properties of undefined (reading 'set')"},{"fix":"First, verify that your Node.js version is explicitly supported by `cls-hooked`'s `engines` field. Second, ensure you are using the latest stable version of `cls-hooked` to benefit from any bug fixes related to `async_hooks` compatibility. If the problem persists, review complex or non-standard asynchronous flows in your application.","cause":"This is a low-level error originating from Node.js's internal `async_hooks` or `AsyncWrap` API. It typically indicates an issue with how `cls-hooked` is interacting with the underlying asynchronous resource tracking, potentially due to an unsupported Node.js version, specific asynchronous patterns, or an internal bug in `cls-hooked`.","error":"Error: The 'this' context of 'AsyncResource' is not an instance of AsyncResource"}],"ecosystem":"npm"}