block/buzz · error · anyhow::Error

project announcement is missing its address

Error message

project announcement is missing its address

What it means

Every project announcement template must carry a d tag with a non-empty value: the d tag is the NIP-33 address coordinate (kind:pubkey:d) that makes the announcement replaceable and resolvable. A template with no d tag, or one whose value is empty or whitespace-only, is rejected before signing.

Source

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

    });
}

fn build_project_owner_announcement_events(
    announcements: Vec<ProjectOwnerAnnouncementTemplate>,
    keys: &nostr::Keys,
) -> Result<Vec<nostr::Event>> {
    let now = nostr::Timestamp::now().as_secs();
    announcements
        .into_iter()
        .map(|template| {
            if !matches!(template.kind, 30_617 | 30_621) {
                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}"))

View on GitHub (pinned to dad5a33865)

Solutions

  1. Add a d tag with a stable, non-empty identifier: [["d", "my-project"]]
  2. Keep the d value stable across restarts so the announcement replaces (NIP-33 LWW) instead of duplicating
  3. Validate the template JSON with a preflight check before deploying the harness

Example fix

# agent config
# before
project_owner_announcements:
  - kind: 30621
    tags: [["name", "my-project"]]
# after
project_owner_announcements:
  - kind: 30621
    tags: [["d", "my-project"], ["name", "my-project"]]
Defensive patterns

Strategy: validation

Validate before calling

// Preflight: every template must carry a non-empty d tag
let ok = templates.iter().all(|t| {
    t.tags.iter().any(|tag| {
        tag.first().is_some_and(|k| k == "d")
            && tag.get(1).is_some_and(|v| !v.trim().is_empty())
    })
});
if !ok { anyhow::bail!("a project announcement template is missing its d tag"); }

Type guard

fn has_address_tag(tags: &[Vec<String>]) -> bool {
    tags.iter().any(|tag| {
        tag.first().is_some_and(|k| k == "d")
            && tag.get(1).is_some_and(|v| !v.trim().is_empty())
    })
}

Prevention

When it happens

Trigger: A project_owner_announcements template whose tags array lacks a ["d", "..."] entry entirely, or contains ["d", ""] / ["d", " "]; the check at crates/buzz-acp/src/lib.rs:1220-1224 fails during event construction at startup.

Common situations: Hand-written config where the author assumed the id field was enough; a template generator that emits tags only for members and forgets the address tag.

Related errors


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