block/buzz · error · anyhow::Error

project announcement timestamp is too far in the future

Error message

project announcement timestamp is too far in the future

What it means

A project announcement's created_at (taken from the template, defaulting to now) must not be more than 300 seconds in the future. This guard prevents far-future timestamps, which would defeat relay last-write-wins ordering and pin the announcement forever.

Source

Thrown at crates/buzz-acp/src/lib.rs:1734

                anyhow::bail!("unsupported project announcement kind");
            }
            if !template.tags.iter().any(|tag| {
                tag.first().is_some_and(|value| value == "d")
                    && tag.get(1).is_some_and(|value| !value.trim().is_empty())
            }) {
                anyhow::bail!("project announcement is missing its address");
            }
            let tags = template
                .tags
                .into_iter()
                .map(|tag| {
                    nostr::Tag::parse(tag)
                        .map_err(|error| anyhow::anyhow!("invalid project tag: {error}"))
                })
                .collect::<Result<Vec<_>>>()?;
            let created_at = template.created_at.unwrap_or(now);
            if created_at > now.saturating_add(300) {
                anyhow::bail!("project announcement timestamp is too far in the future");
            }
            nostr::EventBuilder::new(nostr::Kind::Custom(template.kind), template.content)
                .tags(tags)
                .custom_created_at(nostr::Timestamp::from(created_at))
                .sign_with_keys(keys)
                .map_err(|error| anyhow::anyhow!("sign project announcement: {error}"))
        })
        .collect()
}

fn emit_project_owner_control_result(
    observer: Option<&observer::ObserverHandle>,
    request_id: &str,
    status: &str,
    events: &[nostr::Event],
    error: Option<String>,
) {
    let Some(observer) = observer else {

View on GitHub (pinned to dad5a33865)

Solutions

  1. Remove created_at from the template so it defaults to the harness's current time
  2. Or set created_at to a value at or before the moment the harness starts
  3. Sync the machine that generates the config and the harness host with NTP

Example fix

# agent config
# before
project_owner_announcements:
  - kind: 30621
    tags: [["d", "my-project"]]
    created_at: 4102444800
# after
project_owner_announcements:
  - kind: 30621
    tags: [["d", "my-project"]]
    # created_at omitted -> defaults to now
Defensive patterns

Strategy: validation

Validate before calling

// Preflight: reject templates whose created_at is more than 5 minutes ahead
let now = std::time::SystemTime::now()
    .duration_since(std::time::UNIX_EPOCH)?.as_secs();
if let Some(ts) = template.created_at {
    if ts > now + 300 { anyhow::bail!("created_at {ts} is more than 300s ahead of host clock {now}"); }
}

Prevention

When it happens

Trigger: A template explicitly sets created_at more than now+300s, or the template's created_at was computed on a machine whose clock runs more than 5 minutes ahead of the harness host's clock.

Common situations: NTP drift on the workstation that authored the config; a CI pipeline with a skewed clock stamping generated templates; daylight-saving or timezone math bugs producing future timestamps.

Related errors


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