{"record":{"id":"d9a7c8fd6ce77b81","repo":"block/buzz","slug":"policy-owner-only-agent-has-no-owner-set","errorCode":null,"errorMessage":"policy:owner_only — agent has no owner set","messagePattern":"policy:owner_only — agent has no owner set","errorType":"validation","errorClass":"IngestError::Rejected","httpStatus":null,"severity":"error","filePath":"crates/buzz-relay/src/handlers/side_effects.rs","lineNumber":426,"sourceCode":"                    ));\n                }\n            }\n\n            // Self-add: always allowed regardless of policy.\n            if target_pubkey == actor_bytes {\n                return Ok(());\n            }\n\n            // Third-party add: check channel_add_policy on the target.\n            if let Some((policy, owner)) = state\n                .db\n                .get_agent_channel_policy(tenant.community(), &target_pubkey)\n                .await?\n            {\n                match policy.as_str() {\n                    \"owner_only\" => {\n                        let owner_bytes = owner.ok_or_else(|| {\n                            anyhow::anyhow!(\"policy:owner_only — agent has no owner set\")\n                        })?;\n                        if actor_bytes != owner_bytes {\n                            return Err(anyhow::anyhow!(\n                                \"policy:owner_only — only the agent owner can add this agent\"\n                            ));\n                        }\n                    }\n                    \"nobody\" => {\n                        return Err(anyhow::anyhow!(\n                            \"policy:nobody — this agent has disabled external channel additions\"\n                        ));\n                    }\n                    // \"anyone\" or any unknown value → allow.\n                    // NOTE: DB ENUM constraint prevents unknown values from being stored.\n                    // If a new policy value is added to the ENUM, update this match.\n                    _ => {}\n                }\n            }","sourceCodeStart":408,"sourceCodeEnd":444,"githubUrl":"https://github.com/block/buzz/blob/f956e6fe06a76e50cbd8fba1a162482e752e7f1a/crates/buzz-relay/src/handlers/side_effects.rs#L408-L444","documentation":"A third-party 9000 add targets an agent pubkey whose channel_add_policy is 'owner_only', but the agent has no registered owner — get_agent_channel_policy() returned a policy with a NULL owner. The relay cannot evaluate who is allowed to add the agent, so it fails closed rather than letting anyone add it. This is a data-integrity gap on the agent's policy row, not a judgement about the actor.","triggerScenarios":"An agent profile/ownership registration was partially completed: channel_add_policy set to owner_only but the agent_owners link never written (or deleted); adding such an agent to any channel by anyone other than the agent itself (self-add returns Ok before the policy check).","commonSituations":"Migration or backfill that populated policy values without owner rows; agent ownership revoked but policy left at owner_only; agents provisioned by tooling that skips the NIP-OA ownership declaration step.","solutions":["Fix the agent's registration: set its NIP-OA owner (agent_owners row in this community) so owner_only has someone to match","Or change the policy to 'anyone' if external adds should be permitted","Have the agent add itself to the channel — self-add bypasses the policy check","Audit other owner_only agents for NULL owners; this error signals a configuration defect, not a permissions request"],"exampleFix":"# before: agent configured owner_only with no owner\n# any third-party 9000 add → policy:owner_only — agent has no owner set\n\n# after: register the owner (or relax the policy)\nbuzz agents set-owner --agent <agent-pubkey> --owner <owner-pubkey>\n# or\nbuzz agents set-add-policy --agent <agent-pubkey> --policy anyone","handlingStrategy":"validation","validationCode":"// Pre-check the agent's policy/owner pair before a third-party add\nlet policy = client.get_agent_channel_policy(community, target_pubkey).await?;\nif policy.as_deref() == Some(\"owner_only\") {\n    let owner = client.get_agent_owner(community, target_pubkey).await?;\n    anyhow::ensure!(owner.is_some(), \"agent is owner_only with no owner — data defect, fix registration\");\n}","typeGuard":null,"tryCatchPattern":"match validate_admin_event(&tenant, 9000, &event, &state).await {\n    Err(e) if e.to_string().contains(\"agent has no owner set\") => {\n        // not retryable by the caller: repair the agent registration (set owner or relax policy), or self-add\n        report_agent_registration_defect(target_pubkey)\n    }\n    other => other,\n}","preventionTips":["Make agent provisioning atomic: policy and owner rows written together or not at all","Run a periodic audit for owner_only agents with NULL owners — each one is a latent failure","Have agents self-add when possible; the self-add path never consults the policy"],"tags":["nostr","nip-29","agents","policy","misconfiguration"],"backgroundTag":"agent-policy-misconfigured","analyzedSha":"f956e6fe06a76e50cbd8fba1a162482e752e7f1a","analyzedAt":"2026-08-16T22:11:40.750Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}