From d2a01dc5583712cbe38f47dbbe9ae29a3ef5373f Mon Sep 17 00:00:00 2001 From: Ethan Arrowood Date: Thu, 27 Aug 2026 14:34:32 -0600 Subject: [PATCH 1/2] docs(configuration): pair threads.preload with preloadRequire for dd-trace `dd-trace/register.js` only installs the ESM loader hooks; it never calls `init()`. Configuring `threads.preload: dd-trace/register.js` alone therefore leaves the tracer uninitialized - spans are created and carry plausible trace ids, but they are no-ops and nothing is exported, so the failure is silent. `dd-trace/init` (`threads.preloadRequire`) is the entry that starts the tracer. Replace the claim that `dd-trace/init` "only covers the main thread", show both keys together in the dd-trace example, and cross-reference the two bullets. Behavior is scoped to dd-trace 6.x, since a future major could change `register.js`. Co-Authored-By: Claude Opus 5 --- reference/configuration/options.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/reference/configuration/options.md b/reference/configuration/options.md index 6163665b..38bbb4a8 100644 --- a/reference/configuration/options.md +++ b/reference/configuration/options.md @@ -65,11 +65,12 @@ threads: - `maxHeapMemory` — Heap limit per thread (MB) - `heapSnapshotNearLimit` — Write a `.heapsnapshot` file when a thread nears its heap limit (loadable in Chrome DevTools Memory tab); _Default_: `false`. See [Worker Thread Debugging](./debugging.md#heap-snapshots-near-the-limit) - `debug` — Enable Node.js inspector; sub-options: `port`, `startingPort`, `host`, `waitForDebugger`. See [Worker Thread Debugging](./debugging.md) -- `preload` — Module, or list of modules, to load (via Node's `--import`) before any Harper or application module on each worker thread. Intended for instrumentation/APM agents that must load first to instrument subsequent module loads. Use the agent's ESM/register entry — e.g. `dd-trace/register.js`, which registers the loader hooks that instrument worker threads (where Harper runs its work); the plain `dd-trace/init` (`--require`) entry only covers the main thread. Bare specifiers resolve against the `node_modules` of your installed [components](../components/overview.md) — so the agent can be shipped as a dependency of a deployed component — and absolute paths are also accepted. Applies to worker threads only (not under Bun). +- `preload` — Module, or list of modules, to load (via Node's `--import`) before any Harper or application module on each worker thread. Intended for instrumentation/APM agents that must load first to instrument subsequent module loads. Use the agent's ESM/register entry — e.g. `dd-trace/register.js`, which installs the ESM loader hooks that produce automatic instrumentation for `import`-loaded modules. As measured on dd-trace 6.x, that entry only registers the loader hooks and never calls `init()`, so `preload` on its own leaves the tracer uninitialized: it still hands out spans with plausible trace ids, but they are no-ops and nothing is ever exported. Pair it with `preloadRequire: dd-trace/init`, which is the entry that actually starts the tracer. Bare specifiers resolve against the `node_modules` of your installed [components](../components/overview.md) — so the agent can be shipped as a dependency of a deployed component — and absolute paths are also accepted. Applies to worker threads only (not under Bun). ```yaml threads: - preload: dd-trace/register.js + preloadRequire: dd-trace/init # starts the tracer + preload: dd-trace/register.js # ESM loader hooks for automatic instrumentation ``` Or several modules: @@ -81,11 +82,11 @@ threads: - /opt/instrumentation/agent.mjs ``` -- `preloadRequire` — Same as `preload`, but loads modules via Node's `--require` (CommonJS) instead of `--import`. Use this for agents that document the `--require` path and do not need ESM loader hooks (e.g. `dd-trace/init`, Dynatrace OneAgent). Same resolution rules as `preload`. +- `preloadRequire` — Same as `preload`, but loads modules via Node's `--require` (CommonJS) instead of `--import`. Use this for agents that document the `--require` path (e.g. `dd-trace/init`, Dynatrace OneAgent). Same resolution rules as `preload`. For dd-trace, `dd-trace/init` is the entry that starts the tracer, and it does not register the ESM loader hooks — keep `preload: dd-trace/register.js` alongside it, as shown under `preload` above. ```yaml threads: - preloadRequire: dd-trace/init + preloadRequire: dd-trace/init # starts the tracer; pair with preload (see above) ``` --- From 38b2178511a0a22767bce648526aa58e9eb68552 Mon Sep 17 00:00:00 2001 From: Ethan Arrowood Date: Fri, 28 Aug 2026 11:27:27 -0600 Subject: [PATCH 2/2] docs(configuration): make every dd-trace preload example viable The multi-module `threads.preload` example still listed `dd-trace/register.js` under `preload` alone, which reproduces the exact inert-tracer footgun the single-module example was corrected for: anyone copying it gets a worker whose tracer is never initialized and which exports no traces. Add `preloadRequire: dd-trace/init` to that example too. Also record why `dd-trace/initialize.mjs` is not the simpler single-entry alternative it looks like. On dd-trace 6.x it gates both its `init()` call and its `Module.register()` of the loader hook behind `isMainThread`, and its exported `load`/`resolve` hooks only take effect under `--loader`. Harper preloads via `--import` in a worker's `execArgv`, so on a worker thread that entry starts nothing and registers nothing. Co-Authored-By: Claude Opus 5 --- reference/configuration/options.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/reference/configuration/options.md b/reference/configuration/options.md index 38bbb4a8..1dec1db4 100644 --- a/reference/configuration/options.md +++ b/reference/configuration/options.md @@ -65,7 +65,7 @@ threads: - `maxHeapMemory` — Heap limit per thread (MB) - `heapSnapshotNearLimit` — Write a `.heapsnapshot` file when a thread nears its heap limit (loadable in Chrome DevTools Memory tab); _Default_: `false`. See [Worker Thread Debugging](./debugging.md#heap-snapshots-near-the-limit) - `debug` — Enable Node.js inspector; sub-options: `port`, `startingPort`, `host`, `waitForDebugger`. See [Worker Thread Debugging](./debugging.md) -- `preload` — Module, or list of modules, to load (via Node's `--import`) before any Harper or application module on each worker thread. Intended for instrumentation/APM agents that must load first to instrument subsequent module loads. Use the agent's ESM/register entry — e.g. `dd-trace/register.js`, which installs the ESM loader hooks that produce automatic instrumentation for `import`-loaded modules. As measured on dd-trace 6.x, that entry only registers the loader hooks and never calls `init()`, so `preload` on its own leaves the tracer uninitialized: it still hands out spans with plausible trace ids, but they are no-ops and nothing is ever exported. Pair it with `preloadRequire: dd-trace/init`, which is the entry that actually starts the tracer. Bare specifiers resolve against the `node_modules` of your installed [components](../components/overview.md) — so the agent can be shipped as a dependency of a deployed component — and absolute paths are also accepted. Applies to worker threads only (not under Bun). +- `preload` — Module, or list of modules, to load (via Node's `--import`) before any Harper or application module on each worker thread. Intended for instrumentation/APM agents that must load first to instrument subsequent module loads. Use the agent's ESM/register entry — e.g. `dd-trace/register.js`, which installs the ESM loader hooks that produce automatic instrumentation for `import`-loaded modules. As measured on dd-trace 6.x, that entry only registers the loader hooks and never calls `init()`, so `preload` on its own leaves the tracer uninitialized: it still hands out spans with plausible trace ids, but they are no-ops and nothing is ever exported. Pair it with `preloadRequire: dd-trace/init`, which is the entry that actually starts the tracer. `dd-trace/initialize.mjs` is not a single-entry shortcut around this pairing: it gates both its `init()` call and its loader-hook registration behind `isMainThread`, so under `--import` on a worker thread it starts nothing and registers nothing. Bare specifiers resolve against the `node_modules` of your installed [components](../components/overview.md) — so the agent can be shipped as a dependency of a deployed component — and absolute paths are also accepted. Applies to worker threads only (not under Bun). ```yaml threads: @@ -73,10 +73,11 @@ threads: preload: dd-trace/register.js # ESM loader hooks for automatic instrumentation ``` -Or several modules: +Or several modules. The `preloadRequire` pairing still applies — an agent listed here is subject to the same rule as when it is the only entry: ```yaml threads: + preloadRequire: dd-trace/init # still what starts the tracer preload: - dd-trace/register.js - /opt/instrumentation/agent.mjs