block/buzz · error · IngestError::Rejected
missing e or a tag for target
Error message
missing e or a tag for target
What it means
validate_standard_deletion_event() rejects a NIP-09 kind:5 deletion event that names no target: has_e_tag() found no 'e' tag at all, and no 'a' tag with content could be located. Buzz treats e/a as the two target classes for deletions — e tags point at concrete event ids, a tags at addressable (kind 30000-39999) coordinates. A deletion with neither is ambiguous and is refused before storage.
Source
Thrown at crates/buzz-relay/src/handlers/side_effects.rs:240
/// Buzz accepts standard deletions for self-authored events, plus the owning
/// human deleting their agent's events (mirrors `validate_edit_ownership`).
/// Channel admin deletions continue to use kind 9005.
pub async fn validate_standard_deletion_event(
tenant: &TenantContext,
event: &Event,
state: &Arc<AppState>,
) -> anyhow::Result<()> {
let actor_bytes = effective_message_author(event, &state.relay_keypair.public_key());
let target_ids = extract_target_event_ids(event);
if !has_e_tag(event) {
// a-tag deletion: verify author owns the addressable event
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 {View on GitHub (pinned to f956e6fe06)
Solutions
- Add at least one ["e", "<64-hex event id>"] tag naming the event(s) to delete
- For addressable events, use ["a", "<kind>:<pubkey-hex>:<d-identifier>"] instead
- Log event.tags before publish and assert the array contains an e or a entry
Example fix
// before
EventBuilder::new(Kind::EventDeletion, "", []).to_event(&keys) // no targets
// after
EventBuilder::new(
Kind::EventDeletion,
"",
[Tag::event(EventId::from_hex("<target-id>")?)],
).to_event(&keys) Defensive patterns
Strategy: validation
Validate before calling
// Client-side pre-publish check mirroring has_e_tag / a-tag lookup
fn has_deletion_target(tags: &[Tag]) -> bool {
tags.iter().any(|t| t.kind().to_string() == "e")
|| tags.iter().any(|t| t.kind().to_string() == "a" && t.content().is_some())
}
assert!(has_deletion_target(&event.tags), "deletion needs an e or a target tag"); Type guard
fn isDeletionTargeted(tags: string[][]): boolean {
return tags.some(t => t[0] === "e") || tags.some(t => t[0] === "a" && !!t[1]);
} Try / catch
match validate_standard_deletion_event(&tenant, &event, &state).await {
Ok(()) => store_and_apply(),
Err(e) if e.to_string().contains("missing e or a tag") => reject_with("deletion must name an e or a target", &event.id),
Err(e) => Err(e),
} Prevention
- Always build kind:5 events through an SDK builder that requires target tags
- Add a unit assertion on tags contents before signing any deletion
- Treat e and a as the only valid target classes; do not invent custom target tag kinds
When it happens
Trigger: Publishing kind:5 with an empty or missing tags list; building the deletion with only an author/metadata tag (e.g. just a 'reason' tag or a malformed tag array); a client bug that serializes tags under the wrong key so 'e'/'a' never reach the relay.
Common situations: Hand-rolled event JSON where the tags array is omitted or misspelled; SDK version change that renamed the tags field; test fixtures copied from a text post instead of a deletion template.
Related errors
- invalid a-tag format
- target event not found
- invalid pubkey in a-tag
- must be event author
- missing or invalid h tag
AI-assisted analysis of block/buzz@f956e6fe06 (2026-08-16).
Data as JSON: /api/errors/8a083fa7bbc3bb1c.
Report an issue: GitHub.