{"record":{"id":"e61b18590260e8d4","repo":"block/buzz","slug":"repo-announcement-rejected-by-community-deletion-f","errorCode":null,"errorMessage":"repo announcement rejected by community deletion fence: {e}","messagePattern":"repo announcement rejected by community deletion fence: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/buzz-relay/src/handlers/side_effects.rs","lineNumber":2677,"sourceCode":"    // pointer failure. An `AlreadyOwned` outcome means the row is owned by some\n    // other attempt (a same-owner sibling, or a prior announce that has since\n    // pushed), and deleting it here would strand a repo whose pointer that\n    // other attempt already established.\n    let reserved_by_this_attempt = matches!(outcome, ReserveOutcome::Reserved);\n\n    // The event row and name registry are ordinary database state: if deletion\n    // quiescing wins before they commit, the DB write fence rejects them; if\n    // they committed first, the destructive DB stage purges them. The manifest\n    // and pointer below are external S3 effects, so acquire the durable\n    // serving-write lease immediately before that sequence. Once acquired,\n    // deletion must drain this lease before it can freeze the final object list.\n    let serving_write = buzz_deletion::acquire_serving_write(\n        &state.db,\n        tenant.community(),\n        \"git_repo_announcement\",\n    )\n    .await\n    .map_err(|e| anyhow::anyhow!(\"repo announcement rejected by community deletion fence: {e}\"))?;\n\n    #[cfg(test)]\n    if let Some(gate) = &hooks.post_lease_gate {\n        gate.reached.notify_one();\n        gate.resume.notified().await;\n    }\n\n    if let Err(error) = serving_write.verify().await {\n        return Err(anyhow::anyhow!(\n            \"repo announcement lost community serving lease: {error}\"\n        ));\n    }\n\n    // Establish/confirm the manifest pointer, keeping the invariant\n    // \"repo announced ⟺ pointer exists\" so the read path can rely on\n    // pointer-absent meaning never-announced (keeping `info_refs`'s fail-closed\n    // `Ok(None) → 404` unambiguous). Two distinct cases:\n    //","sourceCodeStart":2659,"sourceCodeEnd":2695,"githubUrl":"https://github.com/block/buzz/blob/dad5a33865fc81a2e55b3b60746632f615ec1e3a/crates/buzz-relay/src/handlers/side_effects.rs#L2659-L2695","documentation":"Before writing a repo announcement, the relay acquires a serving lease via buzz_deletion::acquire_serving_write. This coordinates with the community deletion fence so writes are rejected while a community deletion is in flight. This error wraps that acquisition failure — the community is (or may be) being deleted, so the announcement write is refused.","triggerScenarios":"Publishing a kind:30617 announcement concurrently with a community deletion operation for the same tenant; a stale or failed deletion run left the fence closed so normal writes cannot acquire the lease.","commonSituations":"An operator deleting a community while clients still announce repos into it; a crashed/interrupted deletion process that never released or reopened the fence; test environments where deletion fence state persists across runs.","solutions":["Retry the announcement after the community deletion completes (or is confirmed cancelled).","Check the deletion fence state for the community; if a deletion is stuck, reconcile/reset the fence via buzz_deletion tooling.","Verify you are announcing into the intended community/tenant — a wrong community id may map to one being deleted.","Inspect the wrapped {e} detail to distinguish 'actively deleting' from 'fence stuck' and act accordingly."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// check fence state before attempting the write\nlet serving = buzz_deletion::serving_state(&state.db, community).await?;\nanyhow::ensure!(serving.is_active(), \"community {community} not actively serving\");","typeGuard":null,"tryCatchPattern":"match acquire_serving_write(&db, community, \"git_repo_announcement\").await {\n    Err(e) if e.to_string().contains(\"deletion fence\") => {\n        tokio::time::sleep(Duration::from_secs(5)).await;\n        // retry after deletion window settles, or surface to operator\n    }\n    r => r,\n}","preventionTips":["Coordinate community deletions with announcement-producing clients (pause writes first).","Monitor stuck deletion fences and alert on long-lived closed fences.","Verify the tenant/community id before publishing announcements."],"tags":["concurrency","fence","relay","deletion"],"backgroundTag":"write-rejected-by-deletion-fence","analyzedSha":"dad5a33865fc81a2e55b3b60746632f615ec1e3a","analyzedAt":"2026-08-30T13:49:18.474Z","contentChangedAt":"2026-08-30T13:49:18.474Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}