Skip to content

Commit ca48c1f

Browse files
committed
chore(error): anchor templates on Google RPC
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
1 parent 41f48a0 commit ca48c1f

5 files changed

Lines changed: 140 additions & 10 deletions

File tree

docs/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Documentation
22

33
- [Document Errors in Proto](how-to/document-errors-in-proto.md)
4+
- [Google RPC Error Templates](explanation/google-rpc-error-template.md)
45
- [Protobuf Extension Naming](explanation/protobuf-extension-naming.md)
56
- [Consistency Pattern](explanation/consistency-pattern.md)
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Google RPC Error Templates
2+
3+
Trogon error options describe the Google RPC rich error details a runtime should emit. They are descriptor metadata, not a replacement for gRPC, Connect, or HTTP error envelopes.
4+
5+
## Why This Exists
6+
7+
Without a shared proto annotation, every runtime has to construct `google.rpc.Status`, `google.rpc.ErrorInfo`, and related details by hand. That makes stable fields such as `domain`, `reason`, and metadata keys easy to drift across languages.
8+
9+
The `trogon.error.v1alpha1` package keeps that contract close to the typed error payload message. Code generators and runtime adapters can read the same descriptor metadata and produce consistent Google RPC details.
10+
11+
## Mapping
12+
13+
| Trogon template field | Google RPC target |
14+
|-----------------------|-------------------|
15+
| `code` | `google.rpc.Status.code` |
16+
| `message` | `google.rpc.Status.message` |
17+
| `domain` | `google.rpc.ErrorInfo.domain` |
18+
| `reason` | `google.rpc.ErrorInfo.reason` |
19+
| `metadata` | `google.rpc.ErrorInfo.metadata` |
20+
| `help_links` | `google.rpc.Help.links` |
21+
22+
Payload fields annotated with `trogon.error.v1alpha1.field` supply emission-specific `ErrorInfo.metadata` values. Template metadata supplies fixed metadata values that apply to every emission.
23+
24+
## Boundaries
25+
26+
Trogon error templates do not declare which RPC can return an error. That belongs in a method-level outcome option.
27+
28+
Trogon error templates also do not model successful outcomes. `trogon.error.v1alpha1.Code` intentionally mirrors only Google RPC error codes and omits `OK`.
29+
30+
The runtime owns protocol adaptation. A gRPC runtime can emit `google.rpc.Status` with typed details. A Connect runtime can expose the same semantics through Connect errors and strongly typed details.
31+
32+
## Example
33+
34+
A template like this:
35+
36+
```proto
37+
message UserNotFoundError {
38+
option (trogon.error.v1alpha1.message).template = {
39+
domain: "identity.trogonstack.dev"
40+
reason: "USER_NOT_FOUND"
41+
message: "The requested user was not found."
42+
code: NOT_FOUND
43+
};
44+
45+
string user_id = 1 [(trogon.error.v1alpha1.field).visibility = VISIBILITY_PUBLIC];
46+
}
47+
```
48+
49+
describes this Google RPC detail identity:
50+
51+
```json
52+
{
53+
"@type": "type.googleapis.com/google.rpc.ErrorInfo",
54+
"domain": "identity.trogonstack.dev",
55+
"reason": "USER_NOT_FOUND",
56+
"metadata": {
57+
"user_id": "usr_123"
58+
}
59+
}
60+
```
61+
62+
The surrounding transport error still comes from the runtime protocol. Trogon only defines the stable error detail contract.
63+
64+
## References
65+
66+
- [Document Errors in Proto](../how-to/document-errors-in-proto.md)
67+
- [Google RPC Status](https://cloud.google.com/tasks/docs/reference/rpc/google.rpc#status)
68+
- [Google RPC ErrorInfo](https://cloud.google.com/spanner/docs/reference/rpc/google.rpc#errorinfo)

docs/explanation/protobuf-extension-naming.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ trogon/stream/v1alpha1/options.proto
108108
109109
trogon/error/v1alpha1/options.proto
110110
├─ MessageOptions { template } → message (870012)
111-
│ └─ Template (nested) { domain, reason, message, code }
111+
│ └─ Template (nested) { domain, reason, message, code, help_links, metadata }
112112
└─ FieldOptions { visibility } → field (870013)
113113
```
114114

docs/how-to/document-errors-in-proto.md

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
This guide shows the intended protobuf shape for typed error payload messages that use `trogon.error.v1alpha1`.
44

5+
Trogon error annotations are descriptor-time templates for the Google RPC rich error model. They describe the `google.rpc.Status` and `google.rpc.ErrorInfo` details a runtime should emit when the typed error occurs.
6+
57
## Example
68

79
```proto
@@ -38,18 +40,68 @@ message ResourceAvailabilityError {
3840
}
3941
```
4042

43+
## Runtime Mapping
44+
45+
When a runtime emits this error, it should build the protocol-native error envelope from the template:
46+
47+
| Trogon option | Google RPC field |
48+
|---------------|------------------|
49+
| `code` | `google.rpc.Status.code` |
50+
| `message` | `google.rpc.Status.message` |
51+
| `domain` | `google.rpc.ErrorInfo.domain` |
52+
| `reason` | `google.rpc.ErrorInfo.reason` |
53+
| `metadata` | `google.rpc.ErrorInfo.metadata` |
54+
| Payload fields | `google.rpc.ErrorInfo.metadata` |
55+
| `help_links` | `google.rpc.Help.links` |
56+
57+
For the example above, a runtime could emit this JSON representation of a `google.rpc.Status`:
58+
59+
```json
60+
{
61+
"code": 8,
62+
"message": "Requested resources are unavailable.",
63+
"details": [
64+
{
65+
"@type": "type.googleapis.com/google.rpc.ErrorInfo",
66+
"reason": "RESOURCE_AVAILABILITY",
67+
"domain": "compute.googleapis.com",
68+
"metadata": {
69+
"component": "compute",
70+
"region": "us-east-1",
71+
"service": "compute-api",
72+
"vm_type": "n2-standard-16",
73+
"zone": "us-east1-b"
74+
}
75+
},
76+
{
77+
"@type": "type.googleapis.com/google.rpc.Help",
78+
"links": [
79+
{
80+
"description": "Compute Docs",
81+
"url": "https://docs.acme.com/compute"
82+
}
83+
]
84+
}
85+
]
86+
}
87+
```
88+
4189
## Notes
4290

4391
- `template` is a message field, so it carries proto3 presence by default.
44-
- The template captures the parts of the error contract that never vary at runtime: `domain`, `reason`, `code`, default `message`, error-level `visibility`, `help_links`, and fixed `metadata` entries.
92+
- The template captures the parts of the Google RPC error contract that never vary at runtime: `domain`, `reason`, `code`, default `message`, error-level `visibility`, `help_links`, and fixed `metadata` entries.
93+
- `domain` and `reason` describe the `google.rpc.ErrorInfo` identity for this error. `reason` must be stable within the `domain`.
4594
- `metadata` declares contract-level constants (component, team, subsystem) that attach to every emission without occupying a wire field. Keys must be unique within the template and must not collide with field names on the payload message.
4695
- Payload fields hold the dynamic context for a single emission. Each field can carry `FieldOptions`:
4796
- `visibility` controls who sees the field at runtime (defaults to `INTERNAL`).
4897
- `value_policy.default_value` substitutes a value when the runtime field is empty.
4998
- `value_policy.value` pins the field to a contract-fixed value; the runtime payload is ignored on emit.
99+
- `visibility` is a Trogon filtering policy. It is not part of `google.rpc.ErrorInfo`.
50100

51101
## References
52102

53103
- [../../proto/trogon/error/v1alpha1/code.proto](../../proto/trogon/error/v1alpha1/code.proto)
54104
- [../../proto/trogon/error/v1alpha1/options.proto](../../proto/trogon/error/v1alpha1/options.proto)
55105
- [../../proto/trogon/error/v1alpha1/visibility.proto](../../proto/trogon/error/v1alpha1/visibility.proto)
106+
- [Google RPC Status](https://cloud.google.com/tasks/docs/reference/rpc/google.rpc#status)
107+
- [Google RPC ErrorInfo](https://cloud.google.com/spanner/docs/reference/rpc/google.rpc#errorinfo)

proto/trogon/error/v1alpha1/options.proto

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,38 +9,44 @@ import "trogon/error/v1alpha1/visibility.proto";
99
option (elixirpb.file).module_prefix = "TrogonProto.Error.V1Alpha1";
1010

1111
// MessageOptions defines message-level options for error payload messages.
12+
//
13+
// These annotations describe the Google RPC rich error details a runtime
14+
// should emit for this payload. They are not a transport envelope; runtimes
15+
// adapt the template into google.rpc.Status, google.rpc.ErrorInfo, google.rpc.Help,
16+
// or equivalent protocol-native details.
1217
message MessageOptions {
1318
// Template defines the static error template for a message that can be
14-
// adapted into a runtime error representation.
19+
// adapted into a runtime Google RPC error representation.
1520
//
1621
// These fields are intentionally language-neutral so both Elixir and Go
1722
// runtimes can derive their native error template APIs from the same proto
1823
// annotation.
1924
message Template {
20-
// domain identifies the logical owner of the error contract.
25+
// domain maps to google.rpc.ErrorInfo.domain.
2126
// Example: "compute.googleapis.com"
2227
string domain = 1;
2328

24-
// reason identifies the stable machine-readable error reason.
29+
// reason maps to google.rpc.ErrorInfo.reason.
2530
// Example: "RESOURCE_AVAILABILITY"
2631
string reason = 2;
2732

28-
// message is the default human-readable message template.
33+
// message maps to google.rpc.Status.message.
2934
string message = 3;
3035

31-
// code is the canonical error code for the template.
36+
// code maps to google.rpc.Status.code.
3237
Code code = 4;
3338

3439
// visibility controls who can see this error at runtime.
3540
// When unset, defaults to INTERNAL for safety.
3641
Visibility visibility = 5;
3742

38-
// help_links provides documentation or support links for this error.
43+
// help_links maps to google.rpc.Help links.
3944
repeated HelpLink help_links = 6;
4045

4146
// metadata declares fixed key/value pairs attached to every emission
42-
// of this error. Unlike struct fields with FieldOptions.value, these
43-
// entries have no wire representation on the payload message — they
47+
// of this error. Runtimes copy these entries into
48+
// google.rpc.ErrorInfo.metadata. Unlike struct fields with FieldOptions.value,
49+
// these entries have no wire representation on the payload message — they
4450
// are part of the error contract itself.
4551
//
4652
// Keys must be unique within a template and must not collide with
@@ -83,6 +89,9 @@ message MessageOptions {
8389
}
8490

8591
// FieldOptions defines field-level options for error payload message fields.
92+
//
93+
// Runtimes copy payload fields into google.rpc.ErrorInfo.metadata unless a
94+
// value policy supplies a default or fixed value.
8695
message FieldOptions {
8796
// visibility controls who can see this metadata field at runtime.
8897
//

0 commit comments

Comments
 (0)