block/buzz · error · anyhow::Error

target event not found

Error message

target event not found

What it means

In the relay's kind:9005 (NIP-29 delete event) side-effect handler, the target message is taken from the event's e tag and looked up with db.get_event_by_id scoped to the community. The path fails closed: if no stored event with that id exists, the delete is rejected before anything is stored — you cannot delete (or tombstone) a message the relay never accepted. This also guards against cross-community ids: an id that exists on another relay is still 'not found' here.

Solutions

  1. Verify the target exists on this relay before sending: query it (buzz messages thread --channel <ch> --event <id>, or POST /query with an ids filter).
  2. If the row was purged by deletion tooling, no delete is needed — the message is already gone; drop the request.
  3. Check the e tag is the full 64-char hex id with no whitespace or truncation.
  4. Confirm client and tooling talk to the same community (same RELAY_URL host) — an id from another community will never resolve.

Example fix

// before: fire-and-forget delete against an id that may be gone
await relay.publish(deleteEvent({ target: maybeStaleId, channel }));
// after: verify existence first, treat 9005 'target event not found' as success for purged ids
const existing = await relay.query({ ids: [targetId], kinds: [MESSAGE_KINDS] });
if (existing.length === 0) { /* already gone; nothing to delete */ }
Defensive patterns

Strategy: validation

Validate before calling

// client-side: confirm the target exists in this community before publishing 9005
const rows = await relay.query({ ids: [targetId], limit: 1 });
if (rows.length === 0) {
  // treat as already-deleted: publish nothing, report success to the user
  return { ok: true, noop: true };
}

Try / catch

try {
  await relay.publish(deleteEvent);
} catch (e) {
  if (String(e).includes('target event not found')) {
    // idempotent delete: the target is gone from this community — swallow
    return { ok: true, noop: true };
  }
  throw e;
}

Prevention

When it happens

Trigger: A client publishes kind:9005 whose e tag references an id that was never stored on this community, was already hard-deleted/purged (e.g., by buzz-deletion), was truncated or mangled during copy, or belongs to a different relay/community.

Common situations: Retrying an old delete after the target was purged by retention tooling; a client replaying a queue of deletes against a fresh/restored database; pointing a staging client at production (or vice versa) so ids never match.

Related errors


AI-assisted analysis of block/buzz@dad5a33865 (2026-09-05). Data as JSON: /api/errors/68b8bc18dd807cd3. Report an issue: GitHub.

Appendix: source

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

        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_for_event_write(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?
        {
            return Err(anyhow::anyhow!("must be event author"));
        }
    }

    Ok(())
}

/// Returns `true` if `actor_bytes` is the NIP-OA owner of **any** active owner-role
/// member in `members`. Used by kind:9002 and kind:9008 to authorize the owning

View on GitHub (pinned to dad5a33865)