Hmbown/CodeWhale · error · anyhow::Error

fire_at must be in the future (got {}, now is {})

Error message

fire_at must be in the future (got {}, now is {})

What it means

create_trigger requires fire_at to be strictly greater than Utc::now() at creation time; equality also fails. The error echoes both timestamps in RFC3339 so clock skew or a stale payload is visible in the message itself.

Source

Thrown at crates/tui/src/automation_manager.rs:1329

                if matches!(
                    run.status,
                    AutomationRunStatus::Queued | AutomationRunStatus::Running
                ) && run.task_id.is_some()
                {
                    pending.push(run);
                }
            }
        }
        Ok(pending)
    }

    // ── Delayed-trigger storage methods ──────────────────────────────────

    /// Persist a new delayed trigger and return the record.
    pub fn create_trigger(&self, req: CreateDelayedTriggerRequest) -> Result<DelayedTriggerRecord> {
        let now = Utc::now();
        if req.fire_at <= now {
            bail!(
                "fire_at must be in the future (got {}, now is {})",
                req.fire_at.to_rfc3339(),
                now.to_rfc3339()
            );
        }
        if req.message.trim().is_empty() {
            bail!("Trigger message must not be empty");
        }
        let record = DelayedTriggerRecord {
            schema_version: CURRENT_TRIGGER_SCHEMA_VERSION,
            trigger_id: format!("trig_{}", Uuid::new_v4().simple()),
            fire_at: req.fire_at,
            message: req.message.trim().to_string(),
            workspace: req.workspace,
            owner_session_id: req.owner_session_id,
            status: DelayedTriggerStatus::Pending,
            created_at: now,
            fired_at: None,

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Compute fire_at as Utc::now() + delay at submission time, not earlier
  2. Drop stored requests whose fire_at has passed instead of replaying them
  3. Sync the system clock (NTP) if the printed 'now' disagrees with expectation
  4. Add a minimum lead (e.g. +30 seconds) to absorb processing delay

Example fix

// before
let fire_at = chrono::Utc::now() - chrono::Duration::minutes(5);

// after
let fire_at = chrono::Utc::now() + chrono::Duration::hours(1);
Defensive patterns

Strategy: validation

Validate before calling

fn fire_at_submittable(fire_at: chrono::DateTime<chrono::Utc>) -> bool {
    fire_at > chrono::Utc::now() + chrono::Duration::seconds(1) // small lead for safety
}

Type guard

fn is_future_timestamp(t: chrono::DateTime<chrono::Utc>) -> bool { t > chrono::Utc::now() }

Try / catch

match manager.create_trigger(req) {
    Ok(trig) => { /* ... */ }
    Err(e) if e.to_string().contains("must be in the future") => {
        // Drop or recompute the timestamp; do not blind-retry the same payload.
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Passing a fire_at already in the past; recomputing fire_at from a clock that is behind; replaying a stored CreateDelayedTriggerRequest after its time has passed; passing exactly now.

Common situations: Retry queues replaying old requests; VMs or containers with drifted clocks; naive local times parsed as UTC and shifted into the past.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/14cac830012beaa1. Report an issue: GitHub.