Title: Dup::TracerMiddleware crashes with NoMethodError: undefined method 'include?' for nil when OTEL_SDK_DISABLED=true
Gem: opentelemetry-instrumentation-rack v0.30.0
Ruby SDK: opentelemetry-sdk v1.10.0
Description
When OTEL_SDK_DISABLED=true is set, every incoming request raises:
NoMethodError: undefined method 'include?' for nil
originating from Dup::TracerMiddleware#untraced_request?. This contradicts the SDK's
documented contract that OTEL_SDK_DISABLED is a seamless no-op swap.
Root Cause
SDK.configure returns immediately when OTEL_SDK_DISABLED=true, so
c.use "OpenTelemetry::Instrumentation::Rack" is never called. The instrumentation is
lazily instantiated but never configured, so Rack::Instrumentation.instance.config
returns {}.
In Dup::TracerMiddleware#initialize:
@untraced_endpoints = config[:untraced_endpoints] # {} -> nil (key absent, no defaults applied)
Then on every request in untraced_request?:
return true if @untraced_endpoints.include?(env['PATH_INFO']) # nil.include? -> crash
When the SDK is enabled, c.use triggers full configuration which applies defaults
(untraced_endpoints: []). The disabled path skips this entirely, leaving
@untraced_endpoints = nil.
Proposed Fix
Either guard the assignment in initialize:
@untraced_endpoints = config[:untraced_endpoints] || []
Or use safe navigation in untraced_request?:
return true if @untraced_endpoints&.include?(env['PATH_INFO'])
Workaround
Skip inserting the middleware when the SDK is disabled:
unless ENV["OTEL_SDK_DISABLED"] == "true"
require "opentelemetry/instrumentation/rack/middlewares/dup/tracer_middleware"
config.middleware.insert_before 0, OpenTelemetry::Instrumentation::Rack::Middlewares::Dup::TracerMiddleware
end
Title: Dup::TracerMiddleware crashes with NoMethodError: undefined method 'include?' for nil when OTEL_SDK_DISABLED=true
Gem: opentelemetry-instrumentation-rack v0.30.0
Ruby SDK: opentelemetry-sdk v1.10.0
Description
When OTEL_SDK_DISABLED=true is set, every incoming request raises:
originating from Dup::TracerMiddleware#untraced_request?. This contradicts the SDK's
documented contract that OTEL_SDK_DISABLED is a seamless no-op swap.
Root Cause
SDK.configure returns immediately when OTEL_SDK_DISABLED=true, so
c.use "OpenTelemetry::Instrumentation::Rack" is never called. The instrumentation is
lazily instantiated but never configured, so Rack::Instrumentation.instance.config
returns {}.
In Dup::TracerMiddleware#initialize:
Then on every request in untraced_request?:
When the SDK is enabled, c.use triggers full configuration which applies defaults
(untraced_endpoints: []). The disabled path skips this entirely, leaving
@untraced_endpoints = nil.
Proposed Fix
Either guard the assignment in initialize:
Or use safe navigation in untraced_request?:
Workaround
Skip inserting the middleware when the SDK is disabled: