{"record":{"id":"b0b0e01aa550c05a","repo":"block/buzz","slug":"owner-p-tag","errorCode":null,"errorMessage":"owner p tag","messagePattern":"owner p tag","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/buzz-db/src/store/channel_members.rs","lineNumber":3173,"sourceCode":"        let member = Keys::generate().public_key().to_bytes();\n        sqlx::query(\n            \"INSERT INTO channel_members (community_id, channel_id, pubkey, role, invited_by) \\\n             VALUES ($1, $2, $3, 'admin', $4)\",\n        )\n        .bind(community_uuid)\n        .bind(channel)\n        .bind(member.as_slice())\n        .bind(owner.as_slice())\n        .execute(&pool)\n        .await\n        .expect(\"seed canonical admin\");\n\n        let roster = |role: &str, timestamp| {\n            EventBuilder::new(Kind::Custom(39002), \"\")\n                .tags(vec![\n                    Tag::parse([\"d\", channel.to_string().as_str()]).expect(\"d tag\"),\n                    Tag::parse([\"p\", hex::encode(owner).as_str(), \"\", \"owner\"])\n                        .expect(\"owner p tag\"),\n                    Tag::parse([\"p\", hex::encode(member).as_str(), \"\", role])\n                        .expect(\"member p tag\"),\n                ])\n                .custom_created_at(Timestamp::from(timestamp))\n                .sign_with_keys(&relay_keys)\n                .expect(\"sign roster\")\n        };\n        let base = Timestamp::now().as_secs();\n        let fresh = roster(\"admin\", base);\n        assert!(\n            db.replace_addressable_event(community, &fresh, Some(channel))\n                .await\n                .expect(\"publish canonical role\")\n                .1\n        );\n        let stale = roster(\"member\", base + 1);\n        let error = db\n            .replace_addressable_event(community, &stale, Some(channel))","sourceCodeStart":3155,"sourceCodeEnd":3191,"githubUrl":"https://github.com/block/buzz/blob/dad5a33865fc81a2e55b3b60746632f615ec1e3a/crates/buzz-db/src/store/channel_members.rs#L3155-L3191","documentation":"The test parses the owner membership p tag with Tag::parse([\"p\", hex::encode(owner), \"\", \"owner\"]). The expect panics when nostr's Tag::parse rejects it: p tags require a valid 32-byte hex pubkey as the second element; an empty recommended-relay element or non-hex/short pubkey fails validation.","triggerScenarios":"Tag::parse([\"p\", hex, \"\", role]) errors when `owner` is not exactly 32 bytes, hex encoding produces wrong length, or the nostr crate enforces p-tag pubkey validity (some versions reject empty third elements or validate the key).","commonSituations":"owner slice built from wrong-length bytes (e.g. truncated hash); crate upgrade tightened p-tag validation to reject empty relay hints; owner accidentally initialized to a zeroed/empty buffer.","solutions":["Assert owner.len() == 32 before building the tag and print hex::encode(owner) on failure","Check the nostr crate version's Tag::parse p-tag rules; adjust the empty \"\" element if the version rejects it","Use a Keys::generate().public_key() derived owner so the bytes are guaranteed valid","Prefer Tag::public_key(owner_pubkey) constructors over raw parse where available"],"exampleFix":"// before\nTag::parse([\"p\", hex::encode(owner).as_str(), \"\", \"owner\"]).expect(\"owner p tag\"),\n// after\nassert_eq!(owner.len(), 32, \"owner pubkey must be 32 bytes\");\nTag::parse([\"p\", hex::encode(owner).as_str(), \"\", \"owner\"]).expect(\"owner p tag\"),","handlingStrategy":"validation","validationCode":"assert_eq!(owner.len(), 32, \"p-tag pubkey must be 32 bytes\");\nassert_eq!(hex::encode(owner).len(), 64);","typeGuard":"fn valid_p_tag(pubkey: &[u8]) -> bool {\n    pubkey.len() == 32 && Tag::parse([\"p\", &hex::encode(pubkey), \"\", \"\"]).is_ok()\n}","tryCatchPattern":"let tag = Tag::parse([\"p\", hex::encode(owner).as_str(), \"\", \"owner\"])\n    .unwrap_or_else(|e| panic!(\"owner p tag invalid (owner={}): {e}\", hex::encode(owner)));","preventionTips":["Derive test pubkeys from Keys::generate() rather than hand-built buffers","Verify byte length before hex encoding into tags","Note crate versions that reject empty relay-hint elements"],"tags":["rust","nostr","tag-parsing","test-code"],"backgroundTag":"nostr-tag-parse-failed","analyzedSha":"dad5a33865fc81a2e55b3b60746632f615ec1e3a","analyzedAt":"2026-08-30T13:49:18.474Z","contentChangedAt":"2026-08-30T13:49:18.474Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}