{"record":{"id":"34806705671bd7ad","repo":"block/buzz","slug":"invalid-action-id-tag","errorCode":null,"errorMessage":"invalid action_id tag","messagePattern":"invalid action_id tag","errorType":"validation","errorClass":"IngestError::Rejected","httpStatus":400,"severity":"error","filePath":"crates/buzz-relay/src/handlers/side_effects.rs","lineNumber":634,"sourceCode":"                    }\n                }\n            } else {\n                // topic/purpose: any member\n                let is_member = state\n                    .is_member_cached(tenant.community(), channel_id, &actor_bytes)\n                    .await?;\n                if is_member {\n                    Ok(())\n                } else {\n                    Err(anyhow::anyhow!(\"not a member\"))\n                }\n            }\n        }\n        9005 => {\n            // DELETE_EVENT: event author OR channel owner/admin.\n            if let Some(action_id) = extract_tag_value(event, \"action_id\") {\n                Uuid::parse_str(&action_id)\n                    .map_err(|_| anyhow::anyhow!(\"invalid action_id tag\"))?;\n            }\n\n            // Extract target event from e tag to check authorship.\n            let target_id = event\n                .tags\n                .iter()\n                .find_map(|tag| {\n                    if tag.kind().to_string() == \"e\" {\n                        tag.content().and_then(|v| hex::decode(v).ok())\n                    } else {\n                        None\n                    }\n                })\n                .ok_or_else(|| anyhow::anyhow!(\"missing e tag for target event\"))?;\n\n            // Verify the target event exists and belongs to the h-tag channel\n            // BEFORE storage. Fail closed: missing target → reject.\n            let target_event = state","sourceCodeStart":616,"sourceCodeEnd":652,"githubUrl":"https://github.com/block/buzz/blob/f956e6fe06a76e50cbd8fba1a162482e752e7f1a/crates/buzz-relay/src/handlers/side_effects.rs#L616-L652","documentation":"Thrown by kind:9005 (DELETE_EVENT) when an optional action_id tag is present but does not parse as a UUID. The relay validates the tag whenever it exists — an absent action_id is fine, but a malformed one is rejected before the delete proceeds.","triggerScenarios":"Publishing kind:9002/9005-style events with [[\"action_id\", \"delete\"]], [[\"action_id\", \"12345\"]], or a non-v4 string. Valid forms are UUIDs like \"9b4c0d04-7a3e-4e2f-9d1a-2f6c8b5e7a11\".","commonSituations":"Clients generating action ids with custom schemes (timestamps, nanoids, ULIDs) instead of UUIDs; copy-paste truncating the UUID; older SDK versions emitting a different id format.","solutions":["Omit the action_id tag if you do not need idempotency tracking — it is optional.","Otherwise generate a real UUID (v4) and send it as a string: [\"action_id\", crypto.randomUUID()].","Check for whitespace/case issues if hand-assembling; the parser is strict."],"exampleFix":"// before\n{ kind: 9005, tags: [[\"h\", ch], [\"e\", targetId], [\"action_id\", `del-${Date.now()}`]] }\n\n// after\n{ kind: 9005, tags: [[\"h\", ch], [\"e\", targetId], [\"action_id\", crypto.randomUUID()]] }","handlingStrategy":"validation","validationCode":"const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\nfunction actionIdTag(id?: string): string[] | null {\n  if (id === undefined) return null;      // omitting it is fine\n  if (!UUID_RE.test(id)) throw new Error(\"action_id must be a UUID — got \" + id);\n  return [\"action_id\", id];\n}","typeGuard":"function isUuid(v: string): boolean {\n  return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(v);\n}","tryCatchPattern":"try {\n  await sdk.publish(deleteEvent);\n} catch (e) {\n  if (String(e).includes(\"invalid action_id tag\")) {\n    delete deleteEvent.tags.find((t) => t[0] === \"action_id\"); // drop the tag and retry\n    await sdk.publish(deleteEvent);\n  } else throw e;\n}","preventionTips":["Generate action ids with crypto.randomUUID() — never timestamps or nanoids.","Remember action_id is optional; omit it unless you need idempotency tracking.","Validate ids from external sources (deep links, logs) before reusing them in events."],"tags":["nostr","validation","kind-9005","uuid-format","relay"],"backgroundTag":"invalid-uuid-format","analyzedSha":"f956e6fe06a76e50cbd8fba1a162482e752e7f1a","analyzedAt":"2026-08-16T22:11:40.750Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}