Skip to main content

Error model

When Andromeda refuses a request, the detail of its problem document comes from the error itself, a typed value created where the fault was found, whose message is written from its own fields. Nothing at the HTTP layer rewrites it. And when the fault is the broker's own, the answer says so and keeps the cause out of your response. This page explains why errors are built that way.

The message belongs to the error​

Each failure is a variant of a typed error enum, and its client-facing text is that variant's display, defined next to it. The fields carry what matters, such as the attribute name, the value you sent and the shape that was expected, so the message is the same every time the same fault happens.

A cause that can take several shapes gets its own enum, one variant per shape, rather than a message built by hand. That keeps the structure, so a caller inside the broker can still tell one fault from another, and the source of a fault stays attached to it until it is logged.

The HTTP layer receives the error, picks the problem type and status for its variant, and passes the message through. It only classifies an error and never words one. It has no wording of its own to add, so two endpoints that hit the same fault say the same thing.

What a message says​

A message states what is wrong, which member, parameter or value it concerns, and what was expected. It uses your vocabulary, id and type rather than @id and @type, and quotes values the way you sent them. It never contains clause numbers or the broker's internal names, and values are never printed in a debugging format that would show the broker's own data structures. Errors shows what that looks like, with the prefixes some families of message carry.

Two of these rules can't be expressed as types, so a check reads every message in the source. The prose starts in lowercase, unless it opens with a value or a specification token, and nothing is interpolated in a debugging format. Checks also fail the build on a free-text error and on a type-erased one. Messages written before the check existed sit on a baseline that can only shrink.

The broker's own faults​

Some faults have nothing to do with your request. A stored row doesn't decode, or an internal invariant breaks. Changing your request wouldn't help, and the details could reveal hostnames, SQL or other internals. So such a fault is logged once, with its full cause, and you get InternalError with a short summary such as internal registration error.

The broker never reports its own fault as a problem with your request. A database outage is a 503 ServiceUnavailable with Retry-After, not a 400, and a row that fails to decode is an error, never read as an empty value. Store outages covers the 503s.

One entity that no longer reads back​

An entity can be stored in a form the running broker no longer reads, for example after an upgrade made decoding stricter. Such a fault belongs to that one entity, so a read that covers many entities doesn't fail because of it. The broker logs the entity and the cause at error level each time it meets it, and handles it according to the read:

  • Query Entities leaves the entity out of the page and adds an NGSILD-Warning with code 299 that names it. The page is still cut and counted as if the entity were there, so NGSILD-Results-Count includes it, and the next-page link appears when it was the entity just past the page.
  • Periodic notifications skip the entity and still notify the others. The keyset walk continues after it.
  • The expiry sweep skips the entity, which stays a candidate, and expires the others. The same happens when one entity's expiry write is refused or can't be built. Only a saturated, unreachable or contended database stops the sweep, because every entity after it would fail the same way.
  • A read of that entity by its identifier, such as Retrieve Entity or the read before a write, still fails with InternalError. A write never starts from a state the broker can't read.
  • A Snapshot query that matches the entity records failure for that query and names the entity. A capture without it wouldn't be the result of the query.

The specification defines NGSILD-Warning for a page that a context source left incomplete. Here the broker's own store left the page incomplete, so the same header reports it. Code 299 is the HTTP warning code for a miscellaneous persistent warning, and this one does persist, because the entity reads no better on the next request.

What the client gets​

A client that receives a typed, precise refusal can fix its request. One that receives a vague message, or a refusal for something the broker did wrong, can't. Keeping the message on the error, and the classification at the edge, is what lets every endpoint refuse the same fault in the same words, with the right status.