block/buzz · error · IngestError::Rejected
must be event author or channel owner/admin
Error message
must be event author or channel owner/admin
What it means
Thrown by kind:9005 (DELETE_EVENT) when the actor is not the message author, not a channel owner/admin, and not the owning human of the agent that authored the target. The author check uses effective_message_author (for relay-signed REST messages the real author is the p tag, not the relay key), and even true authors fall through to this check when they are no longer members of a private channel.
Source
Thrown at crates/buzz-relay/src/handlers/side_effects.rs:711
// Not a member and channel is private — fall through to owner/admin/owner-of-agent check.
}
// Not the author, or author who is no longer a member of a private channel —
// must be owner/admin or the owning human of the message's agent-author.
let members = state.db.get_members(tenant.community(), channel_id).await?;
if actor_is_channel_owner_or_admin(&members, &actor_bytes) {
Ok(())
} else {
// Allow the owning human of the agent that authored the target message,
// even when the human is not a channel member.
if state
.db
.is_agent_owner(tenant.community(), &author, &actor_bytes)
.await?
{
Ok(())
} else {
Err(anyhow::anyhow!(
"must be event author or channel owner/admin"
))
}
}
}
9008 => {
// DELETE_GROUP: owner only, or the owning human of the channel's agent-owner.
let members = state.db.get_members(tenant.community(), channel_id).await?;
let actor_member = members.iter().find(|m| m.pubkey == actor_bytes);
match actor_member {
Some(m) if m.role == "owner" => Ok(()),
_ => {
// Allow the owning human of any active owner-role agent in the
// channel, even when the human is not a channel member —
// diverges from kind:9001 intentionally.
if actor_owns_any_owner_agent(state, tenant.community(), &members, &actor_bytes)
.await?
{View on GitHub (pinned to f956e6fe06)
Solutions
- Delete your own messages with the same keypair that authored them (for relay-signed REST messages, the key in the message's p tag).
- For others' messages, publish with an owner/admin key or ask one to perform the deletion.
- If the message was authored by your agent, ensure is_agent_owner(community, author, your_key) holds — re-register the agent under your key if the mapping is stale.
- If you authored it but were removed from a private channel, you cannot self-delete anymore — an owner/admin must do it.
Example fix
# before — human key deletes an agent-authored message they don't own BUZZ_PRIVATE_KEY=$HUMAN_KEY buzz messages delete --channel $CH --event $ID # → "must be event author or channel owner/admin" # after — publish as the author key (the agent's), or as an admin BUZZ_PRIVATE_KEY=$AGENT_KEY buzz messages delete --channel $CH --event $ID # author path BUZZ_PRIVATE_KEY=$OWNER_KEY buzz messages delete --channel $CH --event $ID # owner/admin path
Defensive patterns
Strategy: validation
Validate before calling
// Verify authorship (or role) before publishing kind:9005
const me = myPubkeys; // includes human key + owned agent keys
const target = await buzz.messagesGet(eventId);
const author = effectiveAuthor(target); // p tag when relay-signed, else pubkey
const members = await buzz.channelsMembersList(channelId);
const myRole = members.find((m) => m.pubkey === myPubkey)?.role;
if (!me.has(author) && myRole !== "owner" && myRole !== "admin") {
throw new Error("Only the author (or an owner/admin) can delete this message");
} Type guard
function canDeleteMessage(
authorPubkey: string,
myKeys: Set<string>,
myRole: "owner" | "admin" | "member" | undefined,
iOwnAuthorAgent: boolean,
): boolean {
if (myKeys.has(authorPubkey)) return true;
if (myRole === "owner" || myRole === "admin") return true;
return iOwnAuthorAgent;
} Try / catch
try {
await sdk.deleteEvent(channelId, eventId);
} catch (e) {
if (String(e).includes("must be event author or channel owner/admin")) {
ui.toast("You can only delete your own messages here — ask a channel admin otherwise");
} else throw e;
} Prevention
- Publish deletes with the same keypair that authored the message (agent key for bot messages).
- For relay-signed REST messages, remember the real author is the p tag inside the event, not the relay key.
- Authors removed from private channels lose self-delete rights — design moderation flows accordingly.
- Keep agent registration (agent-owner mapping) current so the owner-of-agent fallback works.
When it happens
Trigger: Publishing 9005 for someone else's message without owner/admin role; using key A to delete a message authored by key B (e.g. agent key vs human key); the original author trying to delete after being removed from a private channel; agent-owner mapping not covering the key you publish with.
Common situations: Humans deleting bot messages with their own key when the bot was registered under a different owner; moderation tools running with a member key; deleting a message you authored from a device with a different keypair; stale agent-owner rows after re-pairing a bot.
Related errors
- actor not authorized for name/about/archived/visibility/ttl
- actor is not an active member
- not a member
- invalid action_id tag
- missing e tag for target event
AI-assisted analysis of block/buzz@f956e6fe06 (2026-08-16).
Data as JSON: /api/errors/e9ba5e28c70d961f.
Report an issue: GitHub.