block/buzz · error
repo announcement lost community serving lease: {error}
Error message
repo announcement lost community serving lease: {error} What it means
After passing the deletion fence and any test gating, the handler re-verifies the serving lease with serving_write.verify() before performing writes. This error means the lease was invalidated between acquisition and verification — i.e. a community deletion fence closed in the interim, so it is no longer safe to write the repo announcement.
Source
Thrown at crates/buzz-relay/src/handlers/side_effects.rs:2686
// and pointer below are external S3 effects, so acquire the durable
// serving-write lease immediately before that sequence. Once acquired,
// deletion must drain this lease before it can freeze the final object list.
let serving_write = buzz_deletion::acquire_serving_write(
&state.db,
tenant.community(),
"git_repo_announcement",
)
.await
.map_err(|e| anyhow::anyhow!("repo announcement rejected by community deletion fence: {e}"))?;
#[cfg(test)]
if let Some(gate) = &hooks.post_lease_gate {
gate.reached.notify_one();
gate.resume.notified().await;
}
if let Err(error) = serving_write.verify().await {
return Err(anyhow::anyhow!(
"repo announcement lost community serving lease: {error}"
));
}
// Establish/confirm the manifest pointer, keeping the invariant
// "repo announced ⟺ pointer exists" so the read path can rely on
// pointer-absent meaning never-announced (keeping `info_refs`'s fail-closed
// `Ok(None) → 404` unambiguous). Two distinct cases:
//
// - Fresh `Reserved` claim → `seed_manifest_pointer` (strict). This creates
// the empty pointer, and correctly *fails* if a non-empty pointer already
// exists for a name we just reserved — that would be a suspicious stale
// pointer from a prior repo lifecycle, not a legitimate re-announce.
// - Same-owner `AlreadyOwned` (re-announce) → `ensure_manifest_pointer`
// (tolerant). A non-empty pointer is the *normal* post-push state, so
// re-announce must accept it untouched; only an absent pointer is
// repaired by seeding. Using the strict seed here would wrongly reject
// every re-announce after the first push.View on GitHub (pinned to dad5a33865)
Solutions
- Retry the whole announcement from scratch — a fresh acquire_serving_write will now correctly fail-fast or succeed if deletion finished.
- Confirm whether the community was deleted; if so, stop publishing announcements to it.
- Reduce work performed between lease acquisition and verify by keeping the handler's pre-write steps short.
- Check wrapped {error} from verify() to confirm the fence-close cause and timing.
Defensive patterns
Strategy: retry
Validate before calling
// re-check the lease immediately before each write
serving_write.verify().await.map_err(|e| anyhow!("lease lost pre-write: {e}"))?; Try / catch
if let Err(error) = serving_write.verify().await {
tracing::warn!("announcement lost lease, retrying: {error}");
return retry_announcement(event, 1).await; // full re-acquire path
} Prevention
- Keep work between lease acquisition and verify minimal to shrink the race window.
- Implement one full retry (fresh acquire) for lease-lost errors.
- Alert on repeated lease-lost errors — indicates concurrent deletions.
When it happens
Trigger: A community deletion starts between acquire_serving_write and the verify() call during handle_git_repo_announcement_inner; long-running pre-lease work (test gates, slow handlers) widens the race window.
Common situations: Concurrent operator deletion racing with a burst of repo announcements; slow announcement processing (large tag sets, slow DB) leaving the lease open long enough for a deletion to interleave.
Related errors
- repo announcement rejected by community deletion fence: {e}
- repo announcement lost community serving lease on release: {
- classify mutation result: {e}
- Read-only relay socket is not connected.
- Relay disconnected for community switch.
AI-assisted analysis of block/buzz@dad5a33865 (2026-08-30).
Data as JSON: /api/errors/47120656a3e76478.
Report an issue: GitHub.