block/buzz · error · anyhow::Error

deletion request is not runnable, is blocked, or is leased…

Error message

deletion request is not runnable, is blocked, or is leased by another executor

What it means

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.

Solutions

  1. Inspect the request's stage, blocked_reason, and lease columns via the store/CLI (list/show) to see which of the three conditions applies
  2. If leased: wait for DEFAULT_LEASE_DURATION to expire or stop the other executor, then retry
  3. If pending approval or blocked: complete the approval/unblock step for that request first
  4. Retry without --id to let claim_next pick up whatever is runnable

Example fix

# before
buzz-deletion run --id <request-id>   # fails: leased/blocked
# after
buzz-deletion show <request-id>         # check stage + lease owner
# then wait for lease expiry or stop the other executor, and re-run
Defensive patterns

Strategy: retry

Validate before calling

-- Pre-flight in an ops script: only run when a claimable request exists
SELECT id, stage, blocked_reason, lease_expires_at, leased_by
FROM deletion_requests
WHERE id = :id
  AND blocked_reason IS NULL
  AND (lease_expires_at IS NULL OR lease_expires_at < now());

Try / catch

// Wrap executor invocations; distinguish 'nothing claimable' from real failures
match run_executor(id).await {
    Err(e) if e.to_string().contains("not runnable") => {
        // transient: lease held or waiting on approval — back off and retry later
        tokio::time::sleep(Duration::from_secs(30)).await;
    }
    Err(e) => return Err(e),
    Ok(n) => { /* ... */ }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of block/buzz@dad5a33865 (2026-08-20). Data as JSON: /api/errors/7eceae3d3636c47d. Report an issue: GitHub.

Appendix: source

Thrown at crates/buzz-deletion/src/lib.rs:978

            return Ok(0);
        }
        let claim = match request_id {
            Some(id) => {
                services
                    .store
                    .claim_specific(id, &executor_id, DEFAULT_LEASE_DURATION)
                    .await?
            }
            None => {
                services
                    .store
                    .claim_next(&executor_id, DEFAULT_LEASE_DURATION)
                    .await?
            }
        };
        let Some(claim) = claim else {
            if mode == LoopMode::Run && !ran {
                anyhow::bail!(
                    "deletion request is not runnable, is blocked, or is leased by another executor"
                );
            }
            return Ok(0);
        };
        ran = true;
        let output = execute_claim(&services, mode, claim, &shutdown).await?;
        print_json(&output)?;
        let failed = output.last_error.is_some() || output.blocked_reason.is_some();
        if mode == LoopMode::Run || shutdown.is_cancelled() || failed {
            return Ok(i32::from(failed));
        }
    }
}

async fn stop_claim_executor(
    services: &Services,
    mode: LoopMode,

View on GitHub (pinned to dad5a33865)