{"record":{"id":"320d3f867e390ccc","repo":"block/buzz","slug":"metrics-exporter-must-install-exactly-once-error","errorCode":null,"errorMessage":"metrics exporter must install exactly once: {error}","messagePattern":"metrics exporter must install exactly once: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/buzz-relay/src/metrics.rs","lineNumber":219,"sourceCode":"        .with_http_listener(([0, 0, 0, 0], port))\n        .build()\n        .map_err(MetricsInstallError::Build)?;\n\n    metrics::set_global_recorder(recorder)\n        .map_err(|_error| MetricsInstallError::RecorderConflict)?;\n    describe_readiness_metrics();\n    describe_db_pool_metrics();\n    tokio::spawn(exporter);\n    Ok(())\n}\n\n/// Install the global metrics recorder and spawn the Prometheus HTTP exporter.\n///\n/// This compatibility entry point preserves the original panic-on-failure API.\n/// New startup code should use [`try_install`] to report typed failures.\npub fn install(port: u16, gauge_idle_timeout_secs: u64) {\n    try_install(port, gauge_idle_timeout_secs)\n        .unwrap_or_else(|error| panic!(\"metrics exporter must install exactly once: {error}\"));\n}\n\n/// Register the frozen readiness metric descriptions with the active recorder.\npub(crate) fn describe_readiness_metrics() {\n    metrics::describe_counter!(\n        \"buzz_readiness_checks_total\",\n        \"Kubernetes health-listener readiness probes by terminal bounded reason\"\n    );\n    metrics::describe_counter!(\n        \"buzz_readiness_dependency_checks_total\",\n        \"Completed readiness dependency attempts by dependency and bounded outcome\"\n    );\n    metrics::describe_histogram!(\n        \"buzz_readiness_check_duration_seconds\",\n        metrics::Unit::Seconds,\n        \"Completed readiness check duration without outcome label multiplication\"\n    );\n    metrics::describe_gauge!(","sourceCodeStart":201,"sourceCodeEnd":237,"githubUrl":"https://github.com/block/buzz/blob/dad5a33865fc81a2e55b3b60746632f615ec1e3a/crates/buzz-relay/src/metrics.rs#L201-L237","documentation":"install() is the legacy panic-on-failure entry point that installs the global metrics recorder and Prometheus exporter exactly once. It delegates to try_install() and panics with 'metrics exporter must install exactly once: {error}' on any failure — typically because a recorder/exporter was already installed, or the port could not be bound.","triggerScenarios":"Calling install() (or relay startup) twice in the same process — e.g. tests installing the recorder then calling main(); or try_install failing because the Prometheus HTTP exporter port is already in use.","commonSituations":"Test binaries where each test calls setup that installs metrics; double invocation of relay main; another process (or a leftover relay) holds the metrics port.","solutions":["Call try_install(port, secs) instead and handle the typed error — it is the recommended API for startup code","Ensure install()/try_install() is invoked exactly once per process (guard in main/init)","If running tests, install metrics once in a shared setup or use a once-guard","Change the metrics port if another process holds it"],"exampleFix":"// before\nmetrics::install(port, gauge_idle_timeout_secs);\n// after\nif let Err(error) = metrics::try_install(port, gauge_idle_timeout_secs) {\n    tracing::error!(%error, \"metrics exporter failed to install\");\n    return Err(error.into());\n}","handlingStrategy":"try-catch","validationCode":"// ensure single install with a process-wide guard\nstatic INIT: OnceLock<()> = OnceLock::new();\nif INIT.set(()).is_err() { return; } // already installed","typeGuard":"null","tryCatchPattern":"match metrics::try_install(port, secs) {\n    Ok(()) => tracing::info!(\"metrics installed\"),\n    Err(e) => tracing::error!(%e, \"metrics install failed\"),\n}","preventionTips":["Prefer try_install over the panic-on-duplicate install() wrapper","Install metrics exactly once in main()/test harness setup","Verify the metrics port is free before startup (check for stale processes)"],"tags":["rust","metrics","panic","port-conflict"],"backgroundTag":"double-initialization-panic","analyzedSha":"dad5a33865fc81a2e55b3b60746632f615ec1e3a","analyzedAt":"2026-09-05T18:13:50.666Z","contentChangedAt":"2026-09-05T18:13:50.666Z","schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}