The validator wired up in schema_validation.go (loading nostr-protocol/registry-of-kinds/schema.yaml) rejects every imeta tag that conforms to https://github.com/nostr-protocol/nips/blob/master/92.md, with:
blocked: schema validation failed: tag[N][M]: invalid tag 'imeta', missing index M
M always equals len(imeta_tag). The validator demands one more entry than the tag has, so any imeta of any length fails — including the canonical example from NIP-92 itself.
What NIP-92 says
▎ The imeta tag is variadic, and each entry is a space-delimited key/value pair.
▎ Each imeta tag MUST have a url, and at least one other field. imeta MAY include any field specified by https://github.com/nostr-protocol/nips/blob/master/94.md. There SHOULD be only one imeta tag per URL.
The spec rules per entry:
- Every entry past index 0 ("imeta") is a single string of the form "key SPACE value".
- The tag is variadic — any number of entries ≥ 2 (i.e. url + ≥ 1 other) is valid. The spec defines no upper bound and no per-position requirement beyond "must include url."
The canonical NIP-92 example (from nips/92.md):
[
"imeta",
"url https://nostr.build/i/my-image.jpg",
"m image/jpeg",
"blurhash eVF$^OI:${M{o#*0-nNFxakD-?xVM}WEWB%iNKxvR-oetmo#R-aen$",
"dim 3024x4032",
"alt A scenic photo overlooking the coast of Costa Rica",
"x <sha256 hash as specified in NIP 94>",
"fallback https://nostrcheck.me/alt1.jpg",
"fallback https://void.cat/alt1.jpg"
]
This is 9 elements (indices 0–8). When sent to wss://pyramid.fiatjaf.com/, the validator returns tag[N][9]: invalid tag 'imeta', missing index 9. The spec's own example does not pass.
Live repro from a real Amethyst publish
The client emitted (8 elements, indices 0–7, every entry well-formed under NIP-92):
[
"imeta",
"url https://npub175fgy25f6gmf5wrtl6c7dp7v6fkwk6an8eemnpqhfxdmjp2tlu0snrfm3q.blossom.band/a27f4c02678fe5390e34580fa1b74c9e44ef161c62819bc798331599f2846d47.jpg",
"x a27f4c02678fe5390e34580fa1b74c9e44ef161c62819bc798331599f2846d47",
"size 168257",
"m image/jpeg",
"dim 1068x1901",
"blurhash ]LI#u#$*~Wj[MyTft7M|WYWByXM{D*%LxuK7t7oJIpj]TLf6RjWVoyJXNGxZt7R+T1s:jaRkbHTKR*t7oeo0pJoff*R+Rj",
"ox a27f4c02678fe5390e34580fa1b74c9e44ef161c62819bc798331599f2846d47"
]
url ✅ present (NIP-92 MUST). At least one additional NIP-94 field ✅ (x, size, m, dim, blurhash, ox). Each entry is a space-delimited key/value pair ✅. No leading/trailing whitespace ✅.
Pyramid response:
blocked: schema validation failed: tag[2][8]: invalid tag 'imeta', missing index 8
When alt text was added (9 elements), the failing index moved to [9]. There is no length at which the validator accepts the tag.
Root cause
The schema:
# nostr-protocol/registry-of-kinds/schema.yaml
_imetatag: &imetatag
name: imeta
next:
type: imeta
required: true
variadic: true
…combined with validateNext in the schema package:
func (v *Validator) validateNext(tag nostr.Tag, index int, this *ContentSpec) (failedIndex int, err error) {
if len(tag) <= index {
if this.Required {
return index, fmt.Errorf("invalid tag '%s', missing index %d", tag[0], index)
}
return -1, nil
}
// … isTrimmed + type-validator pass …
if this.Variadic {
if len(tag) >= index { // ← off-by-one
return v.validateNext(tag, index+1, this)
}
}
// …
}
Trace for any well-formed imeta (length L, indices 0..L-1), entering at index=1 with {Required: true, Variadic: true}:
- index=1..L-1: each value passes the type validator, then hits the Variadic branch. len(tag) >= index is true, recurses with index+1.
- index=L: len(tag) <= index (L <= L) is true. Required: true ⇒ returns "missing index L".
So the validator's recursion always overshoots by one when Variadic && Required. NIP-92 places no lower bound on the variadic tail length — it only mandates url + at least one other field, both of which are validated
before the recursion ever reaches the overshoot.
Why this is a spec violation
NIP-92 says the tag is variadic. A variadic field, by definition, accepts any non-negative count past its required minimum. The pyramid validator instead behaves as if every position past the start were individually
required — which contradicts the variadic semantics and is unsatisfiable (any extra entry would just push M higher).
The validator effectively makes NIP-92's example unpublishable on this relay, which is a regression that would propagate to every client author that targets the spec example as a conformance test.
Affected clients
Reproduced on Amethyst (Android, latest debug build). Because the failing path is "any well-formed NIP-92 tag," every Nostr client that publishes image notes — Damus, Snort, Iris, Coracle, primal, gossip, Amethyst, etc. —
will be blocked at pyramid for this reason.
Environment
Sources used in this draft:
The validator wired up in schema_validation.go (loading nostr-protocol/registry-of-kinds/schema.yaml) rejects every imeta tag that conforms to https://github.com/nostr-protocol/nips/blob/master/92.md, with:
blocked: schema validation failed: tag[N][M]: invalid tag 'imeta', missing index M
M always equals len(imeta_tag). The validator demands one more entry than the tag has, so any imeta of any length fails — including the canonical example from NIP-92 itself.
What NIP-92 says
▎ The imeta tag is variadic, and each entry is a space-delimited key/value pair.
▎ Each imeta tag MUST have a url, and at least one other field. imeta MAY include any field specified by https://github.com/nostr-protocol/nips/blob/master/94.md. There SHOULD be only one imeta tag per URL.
The spec rules per entry:
The canonical NIP-92 example (from nips/92.md):
This is 9 elements (indices 0–8). When sent to wss://pyramid.fiatjaf.com/, the validator returns tag[N][9]: invalid tag 'imeta', missing index 9. The spec's own example does not pass.
Live repro from a real Amethyst publish
The client emitted (8 elements, indices 0–7, every entry well-formed under NIP-92):
url ✅ present (NIP-92 MUST). At least one additional NIP-94 field ✅ (x, size, m, dim, blurhash, ox). Each entry is a space-delimited key/value pair ✅. No leading/trailing whitespace ✅.
Pyramid response:
blocked: schema validation failed: tag[2][8]: invalid tag 'imeta', missing index 8
When alt text was added (9 elements), the failing index moved to [9]. There is no length at which the validator accepts the tag.
Root cause
The schema:
…combined with validateNext in the schema package:
Trace for any well-formed imeta (length L, indices 0..L-1), entering at index=1 with {Required: true, Variadic: true}:
So the validator's recursion always overshoots by one when Variadic && Required. NIP-92 places no lower bound on the variadic tail length — it only mandates url + at least one other field, both of which are validated
before the recursion ever reaches the overshoot.
Why this is a spec violation
NIP-92 says the tag is variadic. A variadic field, by definition, accepts any non-negative count past its required minimum. The pyramid validator instead behaves as if every position past the start were individually
required — which contradicts the variadic semantics and is unsatisfiable (any extra entry would just push M higher).
The validator effectively makes NIP-92's example unpublishable on this relay, which is a regression that would propagate to every client author that targets the spec example as a conformance test.
Affected clients
Reproduced on Amethyst (Android, latest debug build). Because the failing path is "any well-formed NIP-92 tag," every Nostr client that publishes image notes — Damus, Snort, Iris, Coracle, primal, gossip, Amethyst, etc. —
will be blocked at pyramid for this reason.
Environment
Sources used in this draft: