{"record":{"id":"d1693383acc5ded7","repo":"astrid-runtime/astrid","slug":"install-prometheus-recorder-e","errorCode":null,"errorMessage":"install Prometheus recorder: {e}","messagePattern":"install Prometheus recorder: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/astrid-gateway/src/metrics.rs","lineNumber":130,"sourceCode":"    static HANDLE: Mutex<Option<PrometheusHandle>> = Mutex::new(None);\n    let mut guard = HANDLE\n        .lock()\n        .map_err(|e| anyhow::anyhow!(\"recorder lock poisoned: {e}\"))?;\n    if let Some(existing) = guard.as_ref() {\n        return Ok(existing.clone());\n    }\n    let handle = PrometheusBuilder::new()\n        // Set the per-route latency histogram's buckets explicitly.\n        // The `Suffix` matcher applies to every series whose name\n        // matches the metric base — same buckets for every\n        // (method, route, status) combo.\n        .set_buckets_for_metric(\n            Matcher::Suffix(METRIC_REQUEST_DURATION_SECONDS.to_string()),\n            DURATION_BUCKETS_SECONDS,\n        )\n        .map_err(|e| anyhow::anyhow!(\"configure histogram buckets: {e}\"))?\n        .install_recorder()\n        .map_err(|e| anyhow::anyhow!(\"install Prometheus recorder: {e}\"))?;\n\n    // Describe every metric the gateway emits so `/metrics` carries\n    // `# HELP` + `# TYPE` lines even before the series sees its\n    // first observation. Touch each counter once at zero so it\n    // appears in the scrape body — `metrics-exporter-prometheus`\n    // only renders series after they've been recorded against.\n    describe_counter!(\n        METRIC_REQUESTS_TOTAL,\n        Unit::Count,\n        \"Total HTTP requests by method+route+status.\"\n    );\n    describe_histogram!(\n        METRIC_REQUEST_DURATION_SECONDS,\n        Unit::Seconds,\n        \"Per-request handler latency by method+route+status.\"\n    );\n    describe_counter!(\n        METRIC_AUTH_FAILURES_TOTAL,","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-gateway/src/metrics.rs#L112-L148","documentation":"After configuring buckets, install_recorder calls PrometheusBuilder::install_recorder() to register the global metrics recorder. If the exporter cannot install (typically a recorder/collector registration conflict in the prometheus registry), the failure is wrapped as 'install Prometheus recorder'. Because the handle is cached in a static, this can only fail on the first call.","triggerScenarios":"First invocation of install_recorder() where PrometheusBuilder::install_recorder() fails — usually because a global recorder is already installed (e.g. another exporter such as the prometheus crate's set_boxed_recorder was used earlier in the process).","commonSituations":"Two metrics libraries both trying to install the global recorder; tests running in the same process where another test installed a recorder first; calling a different install path (prometheus::default_registry tricks) before this one.","solutions":["Ensure install_recorder() is called exactly once, early in process startup, before any other metrics installation","Remove or unify competing global recorder installations (only one global recorder is allowed per process)","Route all metrics setup through install_recorder so the idempotent static handle is used","In tests, isolate recorder installation to one test or use a single shared handle"],"exampleFix":"// before\nprometheus::default_registry(); // or another lib installs a global recorder first\nlet handle = metrics::install_recorder()?;\n// after\nlet handle = metrics::install_recorder()?; // single, idempotent installation point","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match metrics::install_recorder() {\n    Ok(h) => h,\n    Err(e) if e.to_string().contains(\"install Prometheus recorder\") => {\n        // another global recorder was installed first; find and remove the competing install\n        Err(anyhow!(\"global recorder conflict: {e}\"))\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Install the global recorder exactly once, at the very start of main","Ban other global recorder installations (grep for set_boxed_recorder / install in deps)","Use the idempotent static-handle path for all later metric setup"],"tags":["prometheus","metrics","initialization"],"backgroundTag":"module-init-failed","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}