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

  1. Set `timeout_until` when enqueuing the timeout action (compute from now + duration).
  2. Audit the actions table for 'timeout' rows with NULL timeout_until and repair or drop them.
  3. Validate the timeout duration input in the decision flow before creating the action.
  4. 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

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.

Related errors


AI-assisted analysis of block/buzz@eed74bde2f (2026-08-30). Data as JSON: /api/errors/c1a429bcf0ff9fb8. Report an issue: GitHub.