Skip to content

_httpPatched guard breaks ESM instrumentation when IITM proxy holds stale snapshots #6489

@andreiborza

Description

@andreiborza

What happened?

The _httpPatched / _httpsPatched guards added in #6437 prevent IITM from patching the ESM proxy for http/https when RITM has already patched them via CJS. This breaks outgoing HTTP span instrumentation for ESM code in environments where the IITM proxy snapshots stale (unpatched) exports—most notably AWS Lambda.

Steps to Reproduce

https://github.com/andreiborza/otel-http-esm-guard-repro

There are three npm scripts demonstrating the issue:

  # 0.213.0 — FAIL (no client span)
  npm run start:213

  # 0.212.0 — PASS (client span created)
  npm run start:212

  # 0.213.0 with proposed fix — PASS (ESM span created + CJS no double spans)
  npm run start:213-patched

Expected Result

Getting http spans for outgoing requests.

Actual Result

Not getting http spans for outgoing requests.

Additional Details

In AWS Lambda with an ESM handler:

  1. --import ./setup.mjs runs—static import 'node:http' pre-caches the ESM namespace with unpatched CJS exports
  2. Lambda runtime (CJS) requires node:http—RITM fires, patches the CJS module, sets _httpPatched = true
  3. Lambda runtime imports the ESM handler—handler does import * as http from 'node:http'
  4. IITM intercepts, creates a proxy wrapping node:http—but the cached ESM namespace has unpatched snapshot values
  5. IITM calls the HttpInstrumentation hook—_httpPatched is true → early return → proxy is not patched
  6. Handler's http.request(...) uses the unpatched snapshot → no client span

The Lambda runtime's RAPIDClient loads http via cjsRequire("node:http"), which goes through Module.prototype.require and triggers RITM.

Suggested fix

Replace the boolean flag with an object identity check:

// Current (breaks ESM when proxy has stale snapshots):
if (this._httpPatched) { return moduleExports; }
this._httpPatched = true;
// Proposed (skips only if it's the exact same object):
if (this._httpPatchedExports === moduleExports) { return moduleExports; }
this._httpPatchedExports = moduleExports;

This preserves the double-span fix from #6428 (same object → skip) while allowing IITM to patch a different proxy object
independently.

The reproduction repo includes a patch file with this fix and npm run start:213-patched tests both:

Tip

React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding +1 or me too, to help us triage it. Learn more here.

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingpkg:instrumentation-httppriority:p2Bugs and spec inconsistencies which cause telemetry to be incomplete or incorrect

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions