{"record":{"id":"86349a1b6a92db26","repo":"block/buzz","slug":"mesh-ready-registry-publish-failed-e","errorCode":null,"errorMessage":"mesh ready-registry publish failed: {e}","messagePattern":"mesh ready-registry publish failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/buzz-relay/src/mesh_boot.rs","lineNumber":462,"sourceCode":"    // foreign and rejected (Wren's review — possession is not authorization).\n    let membership = MeshMembership::new(local_record)\n        .with_expected_relay_pubkey(relay_keypair.public_key().to_hex());\n\n    let registry = ReadyRegistry::new(redis_pool.clone(), config.mesh.registry_refresh);\n    let ready_record = ReadyRecord::new(\n        runtime_id,\n        relay_keypair,\n        addrs,\n        PROTO_VERSION,\n        capabilities(),\n    );\n\n    // First publish is part of boot: if Redis can't take the attested record,\n    // peers can never find us — fail loudly now, not quietly forever.\n    registry\n        .publish_ready(&ready_record)\n        .await\n        .map_err(|e| anyhow::anyhow!(\"mesh ready-registry publish failed: {e}\"))?;\n    tracing::info!(runtime_id = %runtime_id, \"mesh ready record published\");\n\n    // Readiness-gated heartbeat: publishes while the relay would pass\n    // readiness, clears the record on ready→not-ready and on shutdown.\n    let hb_flag = Arc::clone(&shutting_down);\n    buzz_relay_mesh::runtime::spawn_registry_heartbeat(\n        registry.clone(),\n        ready_record,\n        Arc::new(move || !hb_flag.load(Ordering::Relaxed)),\n    );\n\n    let runtime = MeshRuntime::start(endpoint, membership, Some(registry));\n    let owners = Arc::new(crate::audio::join::HuddleOwnerRegistry::new());\n    // Dial seed peers now rather than waiting for the first reconcile tick.\n    runtime.reconcile_now().await;\n\n    // Drain watcher: SIGTERM flips `shutting_down`; gossip `draining=true` so\n    // peers stop routing new sessions here, then actively drain locally-owned","sourceCodeStart":444,"sourceCodeEnd":480,"githubUrl":"https://github.com/block/buzz/blob/f956e6fe06a76e50cbd8fba1a162482e752e7f1a/crates/buzz-relay/src/mesh_boot.rs#L444-L480","documentation":"After the mesh endpoint binds, boot_mesh builds a signed ReadyRecord (attested with the relay keypair) and publishes it to Redis: ReadyRegistry::publish_ready (registry.rs:182) self-verifies the attestation, JSON-encodes the record, and runs SET <ready-key> <payload> EX <ttl> over a deadpool-redis connection. The first publish is deliberately part of boot — the comment in mesh_boot.rs says if Redis cannot take the attested record, peers can never find this relay, so startup fails loudly instead of running silently undiscoverable. Errors surface as MeshError (connection/pool/Redis failures or attestation verification).","triggerScenarios":"BUZZ_MESH=on with REDIS_URL (default redis://localhost:6379, config.rs:515) pointing at a Redis that is down, firewalled, ACL/auth-mismatched, or a deadpool that cannot hand out a connection; TLS scheme mismatch (rediss:// vs redis://); attestation self-verification failing (practically a bug, since the record is generated and signed in the same boot path).","commonSituations":"Enabling the mesh before Redis is reachable; k8s NetworkPolicy blocking relay-to-Redis traffic; Redis restarted with requirepass/ACL changes; compose files where redis starts later than the relay and there is no health-check dependency.","solutions":["From the relay's container/host, verify Redis end-to-end: redis-cli -u \"$REDIS_URL\" ping.","Fix REDIS_URL (host, port, auth, rediss:// for TLS) and confirm the ACL user can SET keys.","If the mesh was not intended, unset BUZZ_MESH — the relay reverts to exact single-instance behavior with no Redis writes.","After Redis is healthy, restart the relay: there is no boot-time retry by design; the readiness-gated heartbeat only maintains the record after the first publish succeeds."],"exampleFix":"# before: BUZZ_MESH=on, REDIS_URL=redis://localhost:6379 (Redis not running)\n#   Error: mesh ready-registry publish failed: ...\n\n# after\nREDIS_URL=redis://redis.internal:6379   # reachable, then restart the relay","handlingStrategy":"validation","validationCode":"// Pre-flight: prove Redis is reachable before enabling the mesh.\nasync fn redis_reachable(url: &str) -> bool {\n    let Ok(client) = redis::Client::open(url) else { return false };\n    let Ok(mut conn) = client.get_connection_manager().await else { return false };\n    redis::cmd(\"PING\").query_async::<String>(&mut conn).await.is_ok()\n}\n\nlet url = std::env::var(\"REDIS_URL\").unwrap_or_else(|_| \"redis://localhost:6379\".into());\nassert!(redis_reachable(&url).await, \"REDIS_URL {url} not reachable — fix it before BUZZ_MESH=on\");","typeGuard":"fn is_redis_conn_err(e: &anyhow::Error) -> bool {\n    let msg = e.to_string().to_lowercase();\n    msg.contains(\"redis\") || msg.contains(\"connection refused\") || msg.contains(\"pool\")\n}","tryCatchPattern":"if let Err(e) = registry.publish_ready(&ready_record).await {\n    tracing::error!(\n        %e,\n        redis_url = std::env::var(\"REDIS_URL\").unwrap_or_default(),\n        \"first ready publish failed — peers cannot discover this relay\"\n    );\n    return Err(anyhow!(\"mesh ready-registry publish failed: {e}\"));\n}","preventionTips":["PING Redis from the relay container's entrypoint before starting with BUZZ_MESH=on","Keep REDIS_URL and BUZZ_MESH in the same env template so they are changed together","Order compose/k8s dependencies so Redis is healthy before the relay starts (health-check gates)"],"tags":["rust","mesh","redis","startup","service-discovery"],"backgroundTag":"redis-connection-failed","analyzedSha":"f956e6fe06a76e50cbd8fba1a162482e752e7f1a","analyzedAt":"2026-08-16T22:11:40.750Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}