block/buzz · error
invalid repo identifier: must be [a-zA-Z0-9._-]{1,64}, no le
Error message
invalid repo identifier: must be [a-zA-Z0-9._-]{1,64}, no leading dots, no '..' What it means
After extracting the `d` tag from a kind:30617 announcement, the relay validates the repo identifier with validate_repo_id. This error means the identifier contains characters outside [a-zA-Z0-9._-], exceeds 64 characters, starts with a dot, or contains the '..' sequence — any of which could enable path traversal or ambiguous repo naming. The announcement is rejected.
Source
Thrown at crates/buzz-relay/src/handlers/side_effects.rs:2571
pub(crate) struct GitRepoAnnouncementGate {
pub(crate) reached: tokio::sync::Notify,
pub(crate) resume: tokio::sync::Notify,
}
pub(crate) async fn handle_git_repo_announcement_inner(
tenant: &TenantContext,
event: &Event,
state: &Arc<AppState>,
hooks: &GitRepoAnnouncementHooks,
) -> anyhow::Result<()> {
#[cfg(not(test))]
let _ = hooks;
// Extract repo identifier from d tag (required for NIP-33 parameterized replaceable events).
let repo_id =
extract_tag_value(event, "d").ok_or_else(|| anyhow::anyhow!("kind:30617 missing d tag"))?;
if !validate_repo_id(&repo_id) {
return Err(anyhow::anyhow!(
"invalid repo identifier: must be [a-zA-Z0-9._-]{{1,64}}, no leading dots, no '..'"
));
}
let owner_hex = hex::encode(event.pubkey.to_bytes());
// The relay holds no persistent per-repo disk state: runtime reads and
// writes hydrate an ephemeral bare repo from object storage per request
// (see `api::git::hydrate`). Announce only (1) reserves the repo name and
// (2) seeds the empty-manifest pointer that makes the repo clone-able.
//
// The `git_repo_names` table (Postgres) is the relay's name registry,
// keyed `(community_id, repo_id)`. It serves three jobs at once inside the
// server-resolved community boundary:
// - uniqueness: `INSERT … ON CONFLICT DO NOTHING` is atomic, so
// concurrent kind:30617 events for the same community/name can't both
// claim it (TOCTOU-free — the DB PK is the race guard);
// - idempotent re-announce: a reservation owned by the same pubkey is anView on GitHub (pinned to dad5a33865)
Solutions
- Rename the repo identifier to contain only [a-zA-Z0-9._-], 1-64 chars.
- Strip leading dots and collapse/remove any '..' sequence in the id before signing.
- Re-publish the kind:30617 event with the corrected `d` tag value.
Example fix
// before [["d", "team/../etc"]] // after [["d", "team-etc"]] // or "team.etc"
Defensive patterns
Strategy: validation
Validate before calling
fn valid_repo_id(id: &str) -> bool {
!id.is_empty() && id.len() <= 64 && !id.starts_with('.')
&& !id.contains("..")
&& id.chars().all(|c| c.is_ascii_alphanumeric() || matches!(c, '.'|'_'|'-'))
} Try / catch
let repo_id = extract_tag_value(&event, "d").context("missing d tag")?;
if !valid_repo_id(repo_id) {
return Err(anyhow!("rejecting announcement: invalid repo id {repo_id:?}"));
} Prevention
- Sanitize repo names (strip slashes/spaces) before placing them in the `d` tag.
- Reject ids with leading dots or '..' at the client, mirroring validate_repo_id.
- Cap repo id length at 64 chars in your tooling.
When it happens
Trigger: Publishing a kind:30617 announcement whose `d` tag value is e.g. "my repo", "repo/name", ".hidden", "a..b", or a >64-char name.
Common situations: Repo names containing slashes or spaces copied from git remote URLs; users trying hierarchical repo ids like "team/project"; legacy repos with dots-leading names; automated scripts interpolating full URLs instead of bare repo names.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- kind:30617 missing d tag
- project announcement is missing its address
- invalid URL {url:?}: {e}
- moderation notice recipient must be a 32-byte pubkey, got {}
- invalid BUZZ_RELAY_PRIVATE_KEY: {e}
AI-assisted analysis of block/buzz@dad5a33865 (2026-08-30).
Data as JSON: /api/errors/545ac9e821087bbc.
Report an issue: GitHub.