Report a Failure in the Body
Table of Contents
+#+filetags: :pattern:nats:error-handling: +#+created: 2026-10-14 +#+updated: 2026-10-14 +#+pattern_group: error-handling
This page defines the nats pattern Report a Failure in the Body in the NATS service patterns language.
1. Also known as
- The canonical response envelope.
2. Context
A handler reached the service layer and the service reached a decision: the row is missing, the version conflicts, a business rule denies the write, or the store threw. Every one of those is a legitimate answer to a well-formed request.
3. Problem
How does a service report what it decided, when "denied" and "failed" and "succeeded" are all normal outcomes of a request it handled?
4. Forces
- A refusal is about the caller's right to ask; a decision is about the request itself, and the caller must be able to tell them apart.
- A caller needs enough structure to branch, not a prose message to parse.
- An exception from the store is a real fault and must not read as a business rule declining.
- The same envelope must serve every entity, so a new entity invents nothing.
5. Solution
Answer with the response type, whose `result` carries the outcome:
auto response = svc.update(*req); // fills result.outcome / code / message reply(nats_, msg, response); // An exception from the store is not a decision: report it as a failure. catch (const std::exception& e) { UpdateResponse failure; failure.result.outcome = ores::utility::domain::outcome::failed; failure.result.code = "internal_error"; failure.result.message = e.what(); reply(nats_, msg, failure); }
The outcome is a closed set, so a caller can be exhaustive:
| Outcome | Means |
|---|---|
ok |
The request succeeded |
invalid |
The request is well-formed but its content is not acceptable |
denied |
A business rule refused it |
missing |
The addressed record does not exist |
conflict |
The version precondition failed |
unavailable |
A dependency the operation needs is not available |
failed |
The operation ran and could not complete |
Two rules:
- The handler decides nothing. It proves the request, checks the permission, decodes the payload, calls the service and carries the answer back. The outcome the caller reads is the service's, so the two cannot disagree.
- A thrown exception becomes
failed, never a business outcome. A store error is not "denied"; collapsing the two hides an outage inside normal answers.
6. Failure handling
| Concern | What the pattern does |
|---|---|
| Errors | Every decided outcome is an answer; the transport reports success. |
| Timeouts | Unaffected: the reply arrives as soon as the service decides. |
| Retries | conflict and unavailable are the outcomes that may be retried; the others will not change. |
| Idempotency | A retry re-runs the service, so the operation's idempotency governs safety. |
| Observability | The handler logs the subject and the outcome, so a decision and its reason are in the log. |
7. Pitfalls
- A failed outcome for a refusal. An authorization failure is reported as
failed, and the caller diagnoses the payload. - A prose message with no code. The caller cannot branch, so it either ignores the failure or parses text.
- An empty message on failure. The log has the outcome and nothing about which field or rule produced it.
- A new outcome value at one call site. The set is closed so callers can be exhaustive; a fresh string breaks that.
8. Resulting context
- Benefits
- One reply shape for every entity, and a caller that can branch on a closed set.
- Liabilities
- "Denied" and "failed" can be confused by a careless handler.
9. In ORE Studio
`ores::utility::domain::result` carries `outcome`, `code`, `message` and
`fields`; every generated handler replies with the response type and maps an
exception to internal_error with outcome failed. See
NATS entity protocol
specification.
10. Related patterns
- Report a Refusal Off the Body — the report for what was not handled.
- Never Let an Exception Cross the Bus — where the failure report is produced.
11. Sources
- NATS entity protocol specification — the response envelope and its outcome set.
- NATS documentation, Request-Reply — the reply mechanism the answer travels on.
12. See also
- NATS service patterns — the structure note that orders this pattern language, and where to read this page in it.