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. Their codes start
envelope.; each format's own refusals keep their module's prefix.
Validation¶
ErrMissingAttribute¶
One of the four required attributes — id, source, specversion, type — is absent or empty.
Returned by Validate, and so by every format's Encode and Decode: 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 binary mode 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¶
time arrived 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. Names must be one or more lower-case ASCII
letters or digits, and not data. The specification's SHOULDs, starting with a letter and staying
under twenty characters, are deliberately not enforced: refusing a name the rest of the world
accepts is the interoperability failure that implementing a standard exists to avoid.
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 the id on the
way out in binary mode, and the event would travel 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.
A format's encoding might 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¶
An attribute or extension value is not valid UTF-8, or a format's own encoding of one does not decode, such as a malformed percent-escape in binary mode. Decoding it leniently would produce a different string than the sender wrote, silently.
Reading and formats¶
ErrUnrecognised¶
Read was given a message no format claims. It is not malformed: it is in no format this reader was
given. If it should have been, the reader's chain is missing a format.
ErrAmbiguous¶
Read was given a message two or more formats claim, such as a CloudEvents structured-mode message
carrying a stray ce-specversion header. Decoding it with either could produce the wrong event, so it is
decoded with neither, and the error names the formats that claimed it.
ErrUnrepresentable¶
A format was asked to encode an event that is valid but that its envelope cannot carry, such as an ID that is not a UUID in an envelope that requires one. CloudEvents' own formats never return it; an interop format's may, and each format's documentation lists what it refuses.
CloudEvents formats¶
Returned by gitlab.com/phpboyscout/go/envelope-cloudevents, and only by it. Their codes keep the
module's prefix, cloudevents., so a counter by code tells the formats apart.
ErrAmbiguousHeader¶
One attribute arrived twice with different values, as two spellings that fold to the same key, or
as two values under one key. A Wire.ContentType that differs from a content-type header counts.
In structured mode, a JSON member that appears twice is refused whatever its values, and so is a body
carrying both data and data_base64.
Only the binding's prefixed headers and content-type are considered. X-Forwarded-For with two
hops is the ordinary shape behind a proxy and is none of the format's business: it is returned in
Foreign.Headers.
Like every other refusal, this one returns the partial event: the ambiguous attribute is untrustworthy, everything else that was readable is not.
ErrReadsAsStructured¶
Binary(b).Encode was given an event whose datacontenttype starts application/cloudevents, the
prefix every binding reserves for structured mode. A reader accepting both modes would refuse it as
ErrAmbiguous, so the publisher hears instead. Publish it with Structured(), or give it a content
type such as application/json.
ErrInvalidJSON¶
A structured-mode body is not one JSON object of CloudEvents members: not an object at all, data
after it, an attribute that is not a string, an extension that is an object or array, or a
data_base64 that is not base64.
What a refusal returns alongside the error¶
Every format's Decode returns the partially-assembled Event, carrying whatever was readable,
including its extensions, and so does Read once a format has claimed the message. ErrUnrecognised
and ErrAmbiguous return an empty event, because nothing was decoded. Log e.ID and e.Source
rather than "a message failed to parse": core NATS has no dead letter, so a refused message is gone
unless something records which one it was.