Skip to content

Errors

Every refusal is a distinct sentinel, created with errors.NewSentinel, so it survives a process boundary and can be counted by kind rather than as "parse failed". Match with errors.Is.

Codec

ErrMissingAttribute

One of the four required attributes — id, source, specversion, type — is absent or empty. Returned by both Marshal and Unmarshal, so a producer learns in its own process that it would emit something no consumer accepts.

Also returned for an extension present with an empty value. An extension is caller-defined, so present-with-nothing is a state the caller chose and one Marshal cannot represent — an empty value writes no header, and the extension would vanish in transit with no error.

ErrUnsupportedSpecVersion

specversion is present and is not 1.0. An absent specversion is ErrMissingAttribute, and the distinction is load bearing: "this is not a CloudEvent at all" and "this is a CloudEvent from a version we do not implement" are different problems, and an operator alerting on version skew wants only the second.

This is the refusal the rest of the strictness follows from: specversion tells a reader how to interpret every other attribute, so continuing past an unrecognised one is guessing at the shape of what is held rather than being lenient about it.

If you see this in production, something is emitting a CloudEvents 0.3 event.

ErrInvalidTime

ce-time is present and is not an RFC 3339 timestamp. Absent is fine; unparseable is not.

ErrDataSchemaNotAbsolute

dataschema is present and is not an absolute URI. The specification types it as URI rather than URI-reference, so unlike source there is no base to resolve a relative one against, and a consumer receiving one could not tell which schema was meant.

ErrInvalidExtensionName

An extension name breaks the specification's naming MUSTs. See Add an extension attribute for the grammar, and note that the SHOULDs are deliberately not enforced.

ErrReservedExtensionName

An extension is named after one of the eight specification attributes — id, source, specversion, type, datacontenttype, dataschema, subject, time.

Separate from ErrInvalidExtensionName because the answer is different: the name is well formed, it is just already taken. Without the refusal, Extensions["id"] would overwrite ce-id on the way out and the event would marshal with a hijacked identity, no error, and a round trip that agrees with itself.

ErrControlCharacter

An attribute value contains a character the specification's String type disallows — U+0000–U+001F or U+007F–U+009F.

Percent-encoding would carry them, which is precisely why they are refused instead. net/http rewrites a newline in a header rather than refusing it, so without the check a receiver would get a different attribute value than the sender set, with nothing erroring anywhere.

ErrInvalidEncoding

A header value is not validly percent-encoded: a truncated escape, a non-hexadecimal one, or a value that decodes to invalid UTF-8.

Both bindings require exactly one round of decoding on receive, so a malformed escape means the sender and this reader disagree about where the value ends. Decoding it leniently would produce a different string than the sender wrote, silently.

ErrAmbiguousHeader

One attribute arrived twice with different values — as two spellings that fold to the same key, or as two values under one key.

Only headers this codec owns are considered. X-Forwarded-For with two hops is the ordinary shape behind a proxy and is none of the codec's business.

Like every other refusal, this one returns the partial event: the ambiguous attribute is untrustworthy, everything else that was readable is not.

URN

Listed in the URN grammar reference alongside the rules they enforce.

What Unmarshal returns alongside an error

Every refusal returns the partially-assembled Event, carrying whatever was readable, including its extensions. Log e.ID and e.Source rather than "a message failed to parse" — see Handle a refused event for why that matters more than it looks.