Skip to content

FastStream bootstrapper NameError when prometheus / opentelemetry extras are not installed #87

@hasansezertasan

Description

@hasansezertasan

Summary

FastStreamBootstrapper in lite-bootstrap 0.28.0 raises NameError at bootstrap time whenever the corresponding optional extra (prometheus or opentelemetry) is not installed. The optional-import guard at module scope hides the missing symbol from check_dependencies(), but two code paths reference the gated symbol unconditionally and trip before any dependency check has a chance to run.

I hit this on a service that intentionally installs only lite-bootstrap[faststream-logging,faststream-sentry,pyroscope] because it runs on NAT'd edge hosts where Prometheus scraping is impractical and there are no downstream hops to trace.

Reproduction

uv venv && uv pip install 'lite-bootstrap[faststream-logging,faststream-sentry]==0.28.0' faststream[rabbit] uvicorn
python - <<'PY'
from faststream.asgi import AsgiFastStream
from faststream.rabbit import RabbitBroker
from lite_bootstrap import FastStreamBootstrapper, FastStreamConfig

config = FastStreamConfig(
    application=AsgiFastStream(RabbitBroker("amqp://guest:guest@localhost/")),
    service_name="repro",
    service_debug=False,
)
FastStreamBootstrapper(config).bootstrap()
PY

Bug 1 — FastStreamPrometheusInstrument.__init__ crashes before check_dependencies()

lite_bootstrap/bootstrappers/faststream_bootstrapper.py:

if import_checker.is_prometheus_client_installed:
    import prometheus_client

def _make_collector_registry() -> "prometheus_client.CollectorRegistry":
    return prometheus_client.CollectorRegistry()  # NameError when extra missing

@dataclasses.dataclass(kw_only=True, frozen=True)
class FastStreamPrometheusInstrument(PrometheusInstrument):
    collector_registry: "prometheus_client.CollectorRegistry" = dataclasses.field(
        default_factory=_make_collector_registry, init=False
    )

BaseBootstrapper._register_or_skip in lite_bootstrap/bootstrappers/base.py:

def _register_or_skip(self, instrument_type):
    instrument = instrument_type(bootstrap_config=...)       # crashes here
    if not instrument.check_dependencies():                  # never reached
        warnings.warn(...)
        return None

The dataclass __init__ evaluates default_factory unconditionally → prometheus_client.CollectorRegistry()NameError: name 'prometheus_client' is not defined. check_dependencies() (which already exists on the class) never gets to short-circuit.

Trace:

File ".../lite_bootstrap/bootstrappers/base.py", line 41, in __init__
    if (instrument := self._register_or_skip(instrument_type)) is not None:
File ".../lite_bootstrap/bootstrappers/base.py", line 45, in _register_or_skip
    instrument = instrument_type(bootstrap_config=self.bootstrap_config)
File "<string>", line 4, in __init__
File ".../lite_bootstrap/bootstrappers/faststream_bootstrapper.py", line 138, in _make_collector_registry
    return prometheus_client.CollectorRegistry()
NameError: name 'prometheus_client' is not defined

Bug 2 — FastStreamHealthChecksInstrument.bootstrap references unbound tracer

lite_bootstrap/bootstrappers/faststream_bootstrapper.py:

if import_checker.is_opentelemetry_installed:
    from opentelemetry import trace
    tracer: typing.Final = trace.get_tracer(__name__)

@dataclasses.dataclass(...)
class FastStreamHealthChecksInstrument(HealthChecksInstrument):
    def bootstrap(self) -> None:
        ...
        if self.bootstrap_config.opentelemetry_generate_health_check_spans:
            check_health = tracer.start_as_current_span(...)(check_health)  # NameError

opentelemetry_generate_health_check_spans defaults to True (instruments/opentelemetry_instrument.py:48), so the branch is taken even when the opentelemetry extra is absent.

Trace:

File ".../lite_bootstrap/bootstrappers/faststream_bootstrapper.py", line 91, in bootstrap
    check_health = tracer.start_as_current_span(f"GET {self.bootstrap_config.health_checks_path}")(
NameError: name 'tracer' is not defined

Expected behavior

Either:

  1. _register_or_skip evaluates check_dependencies() (already a @staticmethod on the prometheus instrument) before instantiating the dataclass, OR
  2. The optional-symbol references (_make_collector_registry, tracer.start_as_current_span(...)) are guarded behind import_checker.is_*_installed at call site.

Either fix would let users install lite-bootstrap with a subset of extras and have the unused instruments skip themselves quietly (which is what the InstrumentDependencyMissingWarning machinery already promises).

Workaround

For Bug 1: subclass FastStreamBootstrapper with instruments_types restricted to installed-extra instruments.

For Bug 2: pass opentelemetry_generate_health_check_spans=False on FastStreamConfig.

Happy to send a PR if a fix shape is agreed.

Environment

  • lite-bootstrap 0.28.0
  • faststream 0.x (rabbit extra)
  • Python 3.14
  • Installed extras: faststream-logging, faststream-sentry, pyroscope

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions