block/buzz · error · IngestError::Rejected

must be event author

Error message

must be event author

What it means

For an 'a'-tag deletion: the coordinate's publisher pubkey is not the deletion's effective author, and is_agent_owner() reports the signer does not own that agent pubkey in this community. Buzz extends NIP-09 so a human may delete their own agent's addressable events (NIP-OA ownership), but a third party deleting someone else's coordinate is refused. Note effective_message_author() unwraps relay-signed attribution (actor/p tags) before comparing.

Source

Thrown at crates/buzz-relay/src/handlers/side_effects.rs:253

        let a_tag = event
            .tags
            .iter()
            .find(|t| t.kind().to_string() == "a")
            .and_then(|t| t.content().map(|s| s.to_string()))
            .ok_or_else(|| anyhow::anyhow!("missing e or a tag for target"))?;
        let parts: Vec<&str> = a_tag.splitn(3, ':').collect();
        if parts.len() < 2 {
            return Err(anyhow::anyhow!("invalid a-tag format"));
        }
        let target_pubkey_bytes =
            hex::decode(parts[1]).map_err(|_| anyhow::anyhow!("invalid pubkey in a-tag"))?;
        if target_pubkey_bytes != actor_bytes
            && !state
                .db
                .is_agent_owner(tenant.community(), &target_pubkey_bytes, &actor_bytes)
                .await?
        {
            return Err(anyhow::anyhow!("must be event author"));
        }
        return Ok(());
    }

    for target_id in target_ids {
        let target_event = state
            .db
            .get_event_by_id_including_deleted(tenant.community(), &target_id)
            .await?
            .ok_or_else(|| anyhow::anyhow!("target event not found"))?;

        let target_author =
            effective_message_author(&target_event.event, &state.relay_keypair.public_key());
        if target_author != actor_bytes
            && !state
                .db
                .is_agent_owner(tenant.community(), &target_author, &actor_bytes)
                .await?

View on GitHub (pinned to f956e6fe06)

Solutions

  1. Sign the deletion with the same key that published the addressable event (the coordinate's pubkey)
  2. If the coordinate belongs to your agent, ensure the agent-owner relationship is registered in this community (is_agent_owner must match), then sign with the owner key
  3. If you are a channel admin deleting someone else's content, use kind 9005 (channel admin deletion) instead of kind 5
  4. Check the coordinate string is not stale — republished events may live under a different pubkey than an earlier version

Example fix

// before: owner key deletes coordinate published by unrelated key
// a = "30078:<other-pubkey>:general" signed by <my-key> → must be event author

// after: sign with the coordinate's own publisher key
let keys = Keys::from(...); // the key whose hex == parts[1]
EventBuilder::new(Kind::EventDeletion, "", [Tag::custom(TagKind::Custom("a"), vec![a])])
    .to_event(&keys)
Defensive patterns

Strategy: validation

Validate before calling

// Before publishing an a-tag deletion, confirm the coordinate's pubkey is yours
// (or an agent you own in this community)
let parts: Vec<&str> = a_value.splitn(3, ':').collect();
let coord_pk = hex::decode(parts[1])?;
let mine = coord_pk == signing_keys.public_key().to_bytes().to_vec();
let my_agent = client.is_agent_owner(community, coord_pk, signing_keys.public_key()).await?;
anyhow::ensure!(mine || my_agent, "cannot delete a coordinate you do not own");

Type guard

fn owns_coordinate(signer_pk: &[u8], coord: &str) -> bool {
    coord
        .splitn(3, ':')
        .nth(1)
        .and_then(|p| hex::decode(p).ok())
        .map(|p| p == signer_pk)
        .unwrap_or(false)
}

Try / catch

match validate_standard_deletion_event(&tenant, &event, &state).await {
    Err(e) if e.to_string().contains("must be event author") && a_tag_path => {
        // pick the key that published the coordinate (or register agent ownership) — do not retry blindly
        prompt_switch_identity(&event);
    }
    other => other,
}

Prevention

When it happens

Trigger: Deleting an addressable event whose coordinate names another user's pubkey; signing the deletion with a fresh key instead of the key that published the coordinate; a human deleting an agent's event without the NIP-OA agent-owner registration linking them in this community.

Common situations: Rotated keys after republishing under a new pubkey; agent events published under the agent key but the owner relationship was never registered via the agent-owner path; deleting across communities — ownership rows are tenant-scoped.

Related errors


AI-assisted analysis of block/buzz@f956e6fe06 (2026-08-16). Data as JSON: /api/errors/3157ed73318f04cf. Report an issue: GitHub.