block/buzz · error
timeout requires timeout_until
Error message
timeout requires timeout_until
What it means
The 'timeout' arm of `run_atomic_mutation` requires `ctx.timeout_until` to be `Some` — a timeout must know when it expires. If the context carries a target but no expiry timestamp, the driver aborts with this error, because executing a timeout without an `until` value would leave the user timed out indefinitely or produce an invalid DB row.
Source
Thrown at crates/buzz-relay/src/handlers/report_resolution.rs:707
.db
.execute_ban_with_marker(
action_id,
lease_token,
ctx.community_id,
target,
ctx.actor_pubkey,
ctx.reason,
)
.await
.map_err(|e| anyhow::anyhow!("ban failed: {e}"))
}
"timeout" => {
let target = ctx
.target_pubkey
.ok_or_else(|| anyhow::anyhow!("timeout requires target_pubkey"))?;
let until = ctx
.timeout_until
.ok_or_else(|| anyhow::anyhow!("timeout requires timeout_until"))?;
state
.db
.execute_timeout_with_marker(
action_id,
lease_token,
ctx.community_id,
target,
ctx.actor_pubkey,
until,
ctx.reason,
)
.await
.map_err(|e| anyhow::anyhow!("timeout failed: {e}"))
}
"kick" => {
let target = ctx
.target_pubkey
.ok_or_else(|| anyhow::anyhow!("kick requires target_pubkey"))?;View on GitHub (pinned to eed74bde2f)
Solutions
- Set `timeout_until` when enqueuing the timeout action (compute from now + duration).
- Audit the actions table for 'timeout' rows with NULL timeout_until and repair or drop them.
- Validate the timeout duration input in the decision flow before creating the action.
- Check serialization between the producer and driver so the timestamp is never dropped (e.g. require the field in the payload type).
Example fix
// before
actions::insert(Action { kind: "timeout", target_pubkey: Some(t), .. })?;
// after
let until = chrono::Utc::now() + chrono::Duration::seconds(duration_secs);
actions::insert(Action { kind: "timeout", target_pubkey: Some(t), timeout_until: Some(until), .. })?; Defensive patterns
Strategy: validation
Validate before calling
let until = ctx.timeout_until.ok_or_else(|| {
anyhow::anyhow!("cannot enqueue timeout: expiry timestamp missing")
})?;
anyhow::ensure!(until > chrono::Utc::now(), "timeout expiry must be in the future"); Type guard
fn timeout_expiry(ctx: &ActionCtx) -> Option<chrono::DateTime<chrono::Utc>> {
ctx.timeout_until
} Try / catch
match run_atomic_mutation(&state, ctx).await {
Err(e) if e.to_string().contains("timeout requires timeout_until") => {
tracing::error!(action_id = %action_id, "timeout action missing expiry: dropping");
mark_action_failed(action_id, "missing timeout_until").await?;
}
Err(e) => return Err(e),
Ok(v) => Ok(v),
} Prevention
- Make timeout_until NOT NULL for timeout actions in the schema.
- Require the duration field in the decision UI/API before creating the action.
- Beware None-vs-missing-key JSON serialization when passing the action between services.
- Test the timeout path with the expiry field explicitly set and unset.
When it happens
Trigger: A timeout enforcement action was created without its expiry timestamp: the decision handler omitted `timeout_until`, the DB column was NULL when leased, or the timestamp was lost in serialization between enqueue and drive.
Common situations: Report-resolution UI/flow allowing a timeout decision with an empty duration field; JSON serialization dropping the timestamp (None vs missing key); manual action rows inserted during testing without expiry; migration or schema change renaming the expiry column.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- ban requires target_pubkey
- timeout requires target_pubkey
- kick requires target_pubkey
- ban failed: {e}
- timeout failed: {e}
AI-assisted analysis of block/buzz@eed74bde2f (2026-08-30).
Data as JSON: /api/errors/c1a429bcf0ff9fb8.
Report an issue: GitHub.