block/buzz · error · IngestError::Rejected
visibility tag must have a value
Error message
visibility tag must have a value
What it means
Thrown when a kind:9002 event has a bare ["visibility"] tag with no value element. Visibility changes must state the target value explicitly; the relay will not guess a default.
Source
Thrown at crates/buzz-relay/src/handlers/side_effects.rs:557
_ => {
return Err(anyhow::anyhow!("channel name is required"));
}
}
}
}
// Validate visibility values before storage.
for t in event.tags.iter() {
if t.kind().to_string() == "visibility" {
match t.content() {
Some("open") | Some("private") => {}
Some(v) => {
return Err(anyhow::anyhow!(
"invalid visibility value: {v} (must be \"open\" or \"private\")"
));
}
None => {
return Err(anyhow::anyhow!("visibility tag must have a value"));
}
}
}
}
// Validate ttl values before storage. Empty string clears the TTL
// (channel becomes permanent); any other value must parse as a
// positive integer number of seconds. A bare tag with no value is
// rejected so clearing is always explicit (`["ttl", ""]`).
for t in event.tags.iter() {
if t.kind().to_string() == "ttl" {
match t.content() {
Some("") => {}
Some(v) => match v.parse::<i32>() {
Ok(n) if n > 0 => {}
_ => {
return Err(anyhow::anyhow!(
"invalid ttl value: {v} (must be a positive integer of seconds, or empty to clear)"View on GitHub (pinned to f956e6fe06)
Solutions
- Emit the explicit value: ["visibility", "private"] or ["visibility", "open"].
- If visibility should not change, omit the visibility tag entirely.
- Add a payload lint for bare single-element tags before publish.
Example fix
// before
{ kind: 9002, tags: [["h", ch], ["visibility"]] }
// after
{ kind: 9002, tags: [["h", ch], ["visibility", "private"]] } Defensive patterns
Strategy: validation
Validate before calling
// Visibility must always carry an explicit value
function buildTags(fields: { visibility?: "open" | "private" }) {
const tags: string[][] = [["h", channelId]];
if (fields.visibility) tags.push(["visibility", fields.visibility]); // omitted entirely when unset
return tags;
} Type guard
function isCompleteTag(t: string[]): boolean {
return t.length >= 2 && typeof t[1] === "string";
} Try / catch
try {
await sdk.publish(editEvent);
} catch (e) {
if (String(e).includes("visibility tag must have a value")) {
throw new Error('Send ["visibility", "open"|"private"] or drop the tag');
} else throw e;
} Prevention
- Build tags from a typed options object where undefined means 'omit the tag'.
- Lint publish payloads for any tag array of length < 2.
- Keep display labels ('Private') separate from wire values ('private').
When it happens
Trigger: Tag arrays built as ["visibility"] without a second element, or serialization paths that drop empty-string values leaving a single-element tag.
Common situations: Toggle helpers that emit the flag name only when the desired state is falsy; defensive payload trimming that removes empty strings; hand-crafted events in tests.
Related errors
- archived tag must have a value
- ttl tag must have a value (seconds, or empty string to clear
- kind:9002 must include at least one metadata tag (name, abou
- invalid archived value: {v} (must be "true" or "false")
- channel name is required
AI-assisted analysis of block/buzz@f956e6fe06 (2026-08-16).
Data as JSON: /api/errors/2418cf51fcd7688d.
Report an issue: GitHub.