Tracer Version(s)
5.105.0, 5.106.0, 5.111.0 (reproduced on these, but likely happening on everything else as well)
Node.js Version(s)
26.4.0
Bug Report
When bundling a Node.js ESM application with the dd-trace/esbuild plugin, the build fails on dd-trace 5.105.0 and later. The failure started after dns/promises instrumentation landed (#8404, shipped in 5.105.0).
For ESM builds, the datadog-esbuild plugin wraps instrumented Node builtins (e.g. node:dns/promises) by generating synthetic modules that register hooks via import-in-the-middle. The generated code uses an absolute filesystem path from require.resolve():
import { register } from '/…/node_modules/.pnpm/import-in-the-middle@3.3.0/node_modules/import-in-the-middle/lib/register.js';
esbuild cannot resolve absolute-path import specifiers during bundling, even when the target file exists on disk. The build fails before any runtime code runs.
This is the same class of bug already fixed for the rewriter in #8526 (dc-polyfill absolute paths → bare specifiers / file:// URLs), but the esbuild plugin still uses JSON.stringify(require.resolve('import-in-the-middle/lib/register.js')) in packages/datadog-esbuild/index.js (~line 357).
Expected: dd-trace/esbuild works with ESM output format on 5.105.0+ (same as 5.104.0).
Actual: esbuild fails with Could not resolve "<absolute path>/import-in-the-middle/lib/register.js".
Bisect:
| dd-trace |
nx run jobs-service:build:production (ESM + dd-trace/esbuild) |
| 5.104.0 |
✅ Passes |
| 5.105.0+ |
❌ Fails on node:dns/promises._dd_esbuild_intercepted |
Suggested fix: Emit a bare specifier instead of an absolute path, e.g.:
import { register } from 'import-in-the-middle/lib/register.js';
…and document that consumers should list import-in-the-middle (and possibly dc-polyfill) in esbuild external, consistent with the existing @datadog/pprof guidance.
Additional context
Reproduction Code
esbuild config (minimal):
'use strict';
const esbuild = require('esbuild');
const ddTracePlugin = require('dd-trace/esbuild');
esbuild
.build({
entryPoints: ['src/main.ts'], // any entry that transitively imports node:dns/promises
bundle: true,
platform: 'node',
format: 'esm',
outfile: 'dist/main.js',
plugins: [ddTracePlugin],
external: ['@datadog/pprof', '@datadog/native-metrics']
})
.catch(err => {
console.error(err);
process.exit(1);
});
package.json:
{
"dependencies": {
"dd-trace": "5.111.0"
}
}
Install with pnpm (isolated node_modules layout makes the absolute path long, but the bug reproduces with any package manager — esbuild rejects absolute imports regardless).
Trigger: Any bundled dependency graph that pulls in node:dns/promises as an ESM import (common via undici / HTTP clients). On 5.104.0 the dns/promises hook did not exist, so the interception path was never hit.
Error Logs
✘ [ERROR] Could not resolve "/…/node_modules/.pnpm/import-in-the-middle@3.3.0/node_modules/import-in-the-middle/lib/register.js"
../../../../_dd_esm_internal_/node:dns/promises._dd_esbuild_intercepted:2:25:
2 │ import { register } from "/…/node_modules/.pnpm/import-in-the-middle@3.3.0/node_modules/import-in-the-middle/lib/register.js";
╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NX Build failed with 1 error:
../../../../_dd_esm_internal_/node:dns/promises._dd_esbuild_intercepted:2:25: ERROR: Could not resolve "/…/node_modules/.pnpm/import-in-the-middle@3.3.0/node_modules/import-in-the-middle/lib/register.js"
With DD_TRACE_DEBUG=true during build, the datadog-esbuild plugin logs resolution of instrumented modules before the failure.
Tracer Config
Runtime tracer is initialized via dd-trace.init() (no custom config object; env-driven):
import ddTrace from 'dd-trace';
if (process.env.NODE_ENV !== 'development' && process.env.NODE_ENV !== 'test') {
ddTrace.init();
}
Relevant environment variables (ECS / production):
DD_SERVICE=jobs-service
DD_ENV=staging|production
DD_PROFILING_ENABLED=true
DD_RUNTIME_METRICS_ENABLED=true
DD_LOGS_INJECTION=true
The failure occurs at bundle time, before the tracer runs.
Operating System
Local dev:
Darwin 24.6.0 (macOS).
Production Docker image:
node:26.4.0-trixie-slim (Debian)
Bundling
ESBuild
Tracer Version(s)
5.105.0, 5.106.0, 5.111.0 (reproduced on these, but likely happening on everything else as well)
Node.js Version(s)
26.4.0
Bug Report
When bundling a Node.js ESM application with the
dd-trace/esbuildplugin, the build fails ondd-trace5.105.0 and later. The failure started afterdns/promisesinstrumentation landed (#8404, shipped in 5.105.0).For ESM builds, the
datadog-esbuildplugin wraps instrumented Node builtins (e.g.node:dns/promises) by generating synthetic modules that register hooks viaimport-in-the-middle. The generated code uses an absolute filesystem path fromrequire.resolve():esbuild cannot resolve absolute-path import specifiers during bundling, even when the target file exists on disk. The build fails before any runtime code runs.
This is the same class of bug already fixed for the rewriter in #8526 (
dc-polyfillabsolute paths → bare specifiers /file://URLs), but the esbuild plugin still usesJSON.stringify(require.resolve('import-in-the-middle/lib/register.js'))inpackages/datadog-esbuild/index.js(~line 357).Expected:
dd-trace/esbuildworks with ESM output format on 5.105.0+ (same as 5.104.0).Actual: esbuild fails with
Could not resolve "<absolute path>/import-in-the-middle/lib/register.js".Bisect:
nx run jobs-service:build:production(ESM +dd-trace/esbuild)node:dns/promises._dd_esbuild_interceptedSuggested fix: Emit a bare specifier instead of an absolute path, e.g.:
…and document that consumers should list
import-in-the-middle(and possiblydc-polyfill) in esbuildexternal, consistent with the existing@datadog/pprofguidance.Additional context
import-in-the-middle/lib/register.jspaths to a bare external specifier (happy to share if useful)Reproduction Code
esbuild config (minimal):
package.json:
{ "dependencies": { "dd-trace": "5.111.0" } }Install with pnpm (isolated
node_moduleslayout makes the absolute path long, but the bug reproduces with any package manager — esbuild rejects absolute imports regardless).Trigger: Any bundled dependency graph that pulls in
node:dns/promisesas an ESMimport(common via undici / HTTP clients). On 5.104.0 thedns/promiseshook did not exist, so the interception path was never hit.Error Logs
With
DD_TRACE_DEBUG=trueduring build, thedatadog-esbuildplugin logs resolution of instrumented modules before the failure.Tracer Config
Runtime tracer is initialized via
dd-trace.init()(no custom config object; env-driven):Relevant environment variables (ECS / production):
The failure occurs at bundle time, before the tracer runs.
Operating System
Local dev:
Darwin 24.6.0 (macOS).
Production Docker image:
node:26.4.0-trixie-slim (Debian)
Bundling
ESBuild