block/buzz · error · IngestError::Rejected
invalid ttl value: {v} (must be a positive integer of second
Error message
invalid ttl value: {v} (must be a positive integer of seconds, or empty to clear) What it means
Thrown when a kind:9002 "ttl" tag value is non-empty but does not parse as a positive i32 number of seconds. Zero, negative numbers, non-numeric strings ("1h", "3600s"), decimals, and values above i32::MAX (2147483647) all fail. The empty string is the only special value — it clears the TTL.
Source
Thrown at crates/buzz-relay/src/handlers/side_effects.rs:574
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)"
));
}
},
None => {
return Err(anyhow::anyhow!(
"ttl tag must have a value (seconds, or empty string to clear)"
));
}
}
}
}
// name/about/archived/visibility/ttl require owner/admin;
// topic/purpose allow any member.
let has_privileged_tag = event.tags.iter().any(|t| {
let k = t.kind().to_string();
k == "name" || k == "about" || k == "archived" || k == "visibility" || k == "ttl"View on GitHub (pinned to f956e6fe06)
Solutions
- Send whole seconds as a base-10 integer string: ["ttl", "3600"] for one hour.
- Convert duration strings client-side (e.g. parse "24h" → 86400) before publishing.
- Clamp to 1..=2147483647; if you want no expiry, use the explicit clear value "" instead of 0.
- Double-check units — the field is seconds, not milliseconds.
Example fix
# before buzz channels update --channel $CH --ttl "24h" # → invalid ttl value # after — whole seconds buzz channels update --channel $CH --ttl 86400 # to make the channel permanent: buzz channels update --channel $CH --ttl ""
Defensive patterns
Strategy: validation
Validate before calling
// Normalize human durations to whole seconds, clamped to i32
const MAX_TTL = 2147483647;
function ttlSeconds(input: string | number): string {
const n = typeof input === "number" ? input : parseInt(input, 10);
if (!Number.isInteger(n) || n <= 0 || n > MAX_TTL) {
throw new Error(`ttl must be an integer 1..${MAX_TTL} (seconds), or "" to clear — got ${input}`);
}
return String(n);
} Type guard
function isValidTtl(v: string): boolean {
if (v === "") return true; // explicit clear
if (!/^\d+$/.test(v)) return false;
const n = Number(v);
return n > 0 && n <= 2147483647;
} Try / catch
try {
await sdk.publish(editEvent);
} catch (e) {
if (String(e).includes("invalid ttl value")) {
throw new Error("TTL must be whole seconds (e.g. 86400 for 24h); use \"\" to make the channel permanent");
} else throw e;
} Prevention
- Always convert duration strings ("24h") and millisecond values to seconds before publishing.
- Clamp the computed ttl = expiry - now to at least 1, or send "" when the channel should be permanent.
- Unit-test the ttl encoder with 0, negative, "1h", and overflow values.
When it happens
Trigger: Publishing [["ttl", "3600s"], ["ttl", "1h"], ["ttl", "0"], ["ttl", "-60"], ["ttl", "86400.5"], or ["ttl", "99999999999"]] in kind:9002.
Common situations: Passing human duration strings from config/env ("24h") straight into the tag; using milliseconds instead of seconds; UI computing ttl = expiry - now and getting 0 or negative for already-expired channels.
Related errors
- 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")
- archived tag must have a value
- channel name is required
AI-assisted analysis of block/buzz@f956e6fe06 (2026-08-16).
Data as JSON: /api/errors/4720b658f242db49.
Report an issue: GitHub.