Skip to content

Instantly share code, notes, and snippets.

@schickling
Last active July 23, 2025 07:00
Show Gist options
  • Save schickling/cf3fae2f25ac0aa7750bac7de88cec5c to your computer and use it in GitHub Desktop.
Save schickling/cf3fae2f25ac0aa7750bac7de88cec5c to your computer and use it in GitHub Desktop.
Investigation: OpenTelemetry Node.js HostDetector import issue in Cloudflare Workers

OpenTelemetry Node.js HostDetector Import Issue in Cloudflare Context

Problem

The Node.js-specific HostDetector from @opentelemetry/resources is being imported in a Cloudflare Workers test environment, causing compatibility issues.

Error Location: /Users/schickling/Code/livestore/cloudflare-adapter/node_modules/.pnpm/@[email protected]_@[email protected]/node_modules/@opentelemetry/resources/build/esm/detectors/platform/node/HostDetector.js

Test File: /Users/schickling/Code/livestore/cloudflare-adapter/tests/integration-cloudflare/src/hibernation.test.ts

Root Cause Analysis

Import Chain

  1. hibernation.test.tsSELF from cloudflare:test
  2. Cloudflare Worker → @livestore/adapter-cloudflaremake-durable-object.ts
  3. make-durable-object.tsmakeAdapter() from ./make-adapter.ts
  4. make-adapter.ts:143Effect.withSpan() (and other Effect utilities)
  5. @livestore/utils/effect/index.ts:3export * as OtelTracer from '@effect/opentelemetry/Tracer'ROOT CAUSE
  6. @effect/opentelemetry/Resource.js:1import * as Resources from "@opentelemetry/resources"
  7. @opentelemetry/resources/index.js:17hostDetector from ./detectors
  8. @opentelemetry/resources/detectors/platform/index.js:16 → Node.js platform detectors
  9. @opentelemetry/resources/detectors/platform/node/HostDetector.jsThe problematic Node.js-specific code

Key Files Involved

/packages/@livestore/utils/src/effect/index.ts:3

export * as OtelTracer from '@effect/opentelemetry/Tracer'  // ← ROOT CAUSE

/packages/@livestore/utils/src/effect/Effect.ts:1,79-90

import * as OtelTracer from '@effect/opentelemetry/Tracer'  // ← Uses OtelTracer

// Used in tapCauseLogPretty function
const span = yield* OtelTracer.currentOtelSpan.pipe(
  Effect.catchTag('NoSuchElementException', (_) => Effect.succeed(undefined)),
)

/packages/@livestore/adapter-cloudflare/src/make-adapter.ts:143

Effect.withSpan('@livestore/adapter-cloudflare:adapter')  // ← Triggers the import chain

/packages/@livestore/adapter-cloudflare/src/make-durable-object.ts:16-21

// This no-op implementation doesn't help because imports happen at module level
const provideOtel =
  (_options: { otelTracer?: any; parentSpanContext?: any }) =>
  <A, E, R>(effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> => {
    return effect  // No-op for Cloudflare Workers
  }

Why the No-Op Doesn't Work

The provideOtel no-op function in make-durable-object.ts doesn't prevent the issue because:

  1. Module-level imports: The @effect/opentelemetry/Tracer import happens when the module is loaded
  2. Transitive dependencies: The import chain goes through multiple levels before reaching the Node.js-specific code
  3. Static analysis: Bundlers and module loaders process all imports regardless of runtime usage

Technical Details

OpenTelemetry Resources Import Structure

@opentelemetry/resources
├── index.js (exports hostDetector)
├── detectors/
│   ├── index.js (re-exports platform detectors)
│   └── platform/
│       ├── index.js (exports from ./node)
│       └── node/
│           ├── HostDetector.js ← PROBLEMATIC FILE
│           ├── OSDetector.js
│           └── ProcessDetector.js

Effect OpenTelemetry Resource Usage

// @effect/opentelemetry/Resource.js:1
import * as Resources from "@opentelemetry/resources";  // Imports ALL detectors including Node.js ones

Known GitHub Issues & Community Solutions

Well-Known GitHub Issues

1. Cloudflare Workers SDK Issue #6581DIRECTLY RELATED

  • Title: "🐛 BUG vitest-pool-workers loads opentelemetry for Node not the browser"
  • URL: cloudflare/workers-sdk#6581
  • Description: When running tests with vitest-pool-workers, it loads the Node.js version of OpenTelemetry instead of the browser/worker-compatible version
  • Status: This is the exact same issue you're experiencing

2. OpenTelemetry JS Issue #4425

  • Title: "How to expose Prometheus metrics for Cloudflare workers (serverless/lambda-like)"
  • URL: open-telemetry/opentelemetry-js#4425
  • Description: Discusses compatibility issues with OpenTelemetry in serverless environments including Cloudflare Workers

3. OpenTelemetry JS Issue #1214

Community Solutions & Workarounds

1. Specialized OpenTelemetry Package for Cloudflare Workers

  • Package: @microlabs/otel-cf-workers (formerly otel-cf-workers)
  • Author: Erwin van der Koogh created this package specifically for Cloudflare Workers
  • Usage:
    npm i @opentelemetry/api @microlabs/otel-cf-workers
  • Benefit: Handles all the compatibility issues with standard OpenTelemetry in Workers environment

2. Required Configuration All solutions require adding Node.js compatibility flag:

# wrangler.toml
compatibility_flags = ["nodejs_compat"]
compatibility_date = "2024-09-23"  # or later

3. Common Compatibility Issues Identified

  • Platform-specific APIs: Standard OpenTelemetry libraries access APIs not available in Workers (window.crypto, performance API)
  • Module Resolution: Node.js built-in modules (http, fs, etc.) aren't available in Workers
  • Resource Detectors: Node.js-specific detectors (HostDetector, ProcessDetector, OSDetector) don't work in Workers
  • XHR and Window References: Some OpenTelemetry implementations use browser-specific APIs that don't work in v8 isolates

Test Environment Specific Issues

The vitest-pool-workers testing environment has specific challenges:

  • It loads Node.js versions of packages instead of worker-compatible versions
  • This affects both development and testing workflows
  • The issue occurs during test execution but not during dev server or deployment

Solutions

1. Conditional Environment-Based Imports (Recommended)

Create separate effect utilities for different environments:

// @livestore/utils/effect/index.cloudflare.ts
export * as OtelTracer from './NoopTracer'  // Use noop tracer for Cloudflare

// @livestore/utils/effect/index.node.ts  
export * as OtelTracer from '@effect/opentelemetry/Tracer'  // Full OpenTelemetry for Node.js

2. Use Cloudflare-Specific OpenTelemetry Package

Replace @effect/opentelemetry with @microlabs/otel-cf-workers:

// For Cloudflare Workers builds
import { trace } from '@microlabs/otel-cf-workers'

3. Create Cloudflare-Specific Effect Module

// @livestore/utils-cloudflare/effect/index.ts
// Re-export everything from utils/effect except OpenTelemetry
export * from '@livestore/utils/effect'
export * as OtelTracer from '@livestore/utils/NoopTracer'  // Override with noop

4. Dynamic Runtime Imports

// Conditional import based on environment
const OtelTracer = typeof globalThis.document === 'undefined' && typeof process \!== 'undefined'
  ? await import('@effect/opentelemetry/Tracer')
  : await import('./NoopTracer')

5. Build-Time Bundler Configuration

Configure your bundler to exclude Node.js-specific OpenTelemetry modules for Cloudflare builds:

// webpack.config.js / esbuild / etc.
externals: {
  '@opentelemetry/resources': 'commonjs @opentelemetry/resources/browser'
}

Recommended Fix

Option 1 (environment-based imports) or Option 2 (using @microlabs/otel-cf-workers) are recommended because they:

  • Cleanly separate concerns between environments
  • Maintain full OpenTelemetry functionality in Node.js
  • Eliminate the import chain issue in Cloudflare
  • Follow established community patterns for this exact problem
  • Address the root cause identified in Cloudflare Workers SDK Issue #6581

Files That Need Changes

  1. /packages/@livestore/utils/src/effect/index.cloudflare.ts (new)
  2. /packages/@livestore/adapter-cloudflare/src/make-adapter.ts (update import)
  3. Build configuration to use the correct entry point per environment
  4. Or alternatively: Replace @effect/opentelemetry with @microlabs/otel-cf-workers for Cloudflare builds

Investigation Date: 2025-01-22
Issue: Node.js HostDetector imported in Cloudflare Workers context
Root Cause: Static import of @effect/opentelemetry/Tracer in utils package
Impact: Build/runtime errors in Cloudflare Workers environment
Community Status: Well-documented issue with established solutions
Related Issue: Cloudflare Workers SDK #6581 (vitest-pool-workers OpenTelemetry compatibility) EOF < /dev/null

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment