{"record":{"id":"7eceae3d3636c47d","repo":"block/buzz","slug":"deletion-request-is-not-runnable-is-blocked-or-i","errorCode":null,"errorMessage":"deletion request is not runnable, is blocked, or is leased by another executor","messagePattern":"deletion request is not runnable, is blocked, or is leased by another executor","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/buzz-deletion/src/lib.rs","lineNumber":978,"sourceCode":"            return Ok(0);\n        }\n        let claim = match request_id {\n            Some(id) => {\n                services\n                    .store\n                    .claim_specific(id, &executor_id, DEFAULT_LEASE_DURATION)\n                    .await?\n            }\n            None => {\n                services\n                    .store\n                    .claim_next(&executor_id, DEFAULT_LEASE_DURATION)\n                    .await?\n            }\n        };\n        let Some(claim) = claim else {\n            if mode == LoopMode::Run && !ran {\n                anyhow::bail!(\n                    \"deletion request is not runnable, is blocked, or is leased by another executor\"\n                );\n            }\n            return Ok(0);\n        };\n        ran = true;\n        let output = execute_claim(&services, mode, claim, &shutdown).await?;\n        print_json(&output)?;\n        let failed = output.last_error.is_some() || output.blocked_reason.is_some();\n        if mode == LoopMode::Run || shutdown.is_cancelled() || failed {\n            return Ok(i32::from(failed));\n        }\n    }\n}\n\nasync fn stop_claim_executor(\n    services: &Services,\n    mode: LoopMode,","sourceCodeStart":960,"sourceCodeEnd":996,"githubUrl":"https://github.com/block/buzz/blob/dad5a33865fc81a2e55b3b60746632f615ec1e3a/crates/buzz-deletion/src/lib.rs#L960-L996","documentation":"The executor tried to lease a deletion request and got nothing: with an explicit id, claim_specific failed because the request does not exist, is not in a claimable state, is blocked, or holds a live lease from another executor (DEFAULT_LEASE_DURATION); without an id, claim_next found zero runnable requests. LoopMode::Run (run-to-completion) treats this as a hard failure instead of idling.","triggerScenarios":"Running the executor in Run mode with --id for a request that is still pending approval, blocked on a dependency, or leased by a concurrent executor whose lease has not expired; or running with no eligible work at all.","commonSituations":"Two executors pointed at the same database (staging leftover process still holds the lease); re-running right after a crashed executor before lease expiry; forgetting that the request needs explicit member approval first.","solutions":["Inspect the request's stage, blocked_reason, and lease columns via the store/CLI (list/show) to see which of the three conditions applies","If leased: wait for DEFAULT_LEASE_DURATION to expire or stop the other executor, then retry","If pending approval or blocked: complete the approval/unblock step for that request first","Retry without --id to let claim_next pick up whatever is runnable"],"exampleFix":"# before\nbuzz-deletion run --id <request-id>   # fails: leased/blocked\n# after\nbuzz-deletion show <request-id>         # check stage + lease owner\n# then wait for lease expiry or stop the other executor, and re-run","handlingStrategy":"retry","validationCode":"-- Pre-flight in an ops script: only run when a claimable request exists\nSELECT id, stage, blocked_reason, lease_expires_at, leased_by\nFROM deletion_requests\nWHERE id = :id\n  AND blocked_reason IS NULL\n  AND (lease_expires_at IS NULL OR lease_expires_at < now());","typeGuard":null,"tryCatchPattern":"// Wrap executor invocations; distinguish 'nothing claimable' from real failures\nmatch run_executor(id).await {\n    Err(e) if e.to_string().contains(\"not runnable\") => {\n        // transient: lease held or waiting on approval — back off and retry later\n        tokio::time::sleep(Duration::from_secs(30)).await;\n    }\n    Err(e) => return Err(e),\n    Ok(n) => { /* ... */ }\n}","preventionTips":["Run exactly one deletion executor per database unless leases are short relative to your retry cadence","Make orchestration scripts stage-aware: check stage/blocked_reason/lease columns before invoking run","Treat a lease as authoritative — never bypass claim_next/claim_specific with direct state writes"],"tags":["deletion","lease","scheduling","cli","concurrency"],"backgroundTag":"job-lease-contention","analyzedSha":"dad5a33865fc81a2e55b3b60746632f615ec1e3a","analyzedAt":"2026-08-20T04:38:24.874Z","contentChangedAt":"2026-08-20T04:38:24.874Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}