Skip to content

Commit b79cee5

Browse files
committed
AIP-193: Update error messages section (including rationale)
Note: this removes the "message alignment" section which I found confusing. We could restore and edit it should we wish.
1 parent 4f75d6a commit b79cee5

1 file changed

Lines changed: 40 additions & 60 deletions

File tree

aip/general/0193.md

Lines changed: 40 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -240,64 +240,36 @@ authentication.
240240

241241
### Error messages
242242

243-
The following sections describe best practices for error messages not
244-
described previously.
243+
Textual error messages can be present in both `Status.message` and
244+
`LocalizedMessage.message` fields. Messages should be succinct but
245+
actionable, with request-specific information (such as a resource name
246+
or region) providing precise details where appropriate. Any request-specific
247+
details **must** be present in [`ErrorInfo.metadata`](#errorinfo).
245248

246249
#### Changing error messages
247250

248-
##### Method
251+
Changing the content of `Status.message` over time must be done carefully,
252+
to avoid breaking clients who have previously had to rely on the message
253+
for all information. See [the rationale section](#updating-statusmessage)
254+
for more details.
249255

250-
The method of changing error messages depends on whether an ErrorInfo
251-
payload is present in the message.
256+
For a given RPC:
252257

253-
If the error message contains ErrorInfo payload (a machine-readable
254-
identifier):
258+
- If the RPC has *always* returned `ErrorInfo` with machine-readable
259+
information, the content of `Status.message` **may** change over time.
260+
(For example, the API producer may provide a clearer explanation,
261+
or more request-specific information.)
262+
- Otherwise, the content of `Status.message` **must** be stable,
263+
providing the same text with the same request-specific information.
264+
Instead of changing `Status.message`, the API **should** include a
265+
[`LocalizedMessage`](#localizedmessage) within `Status.details`.
255266

256-
- `Status.message` *string* and `LocalizedMessage.message` *string* can
257-
change
258-
- New metadata fields can be added
259-
- However, existing metadata fields **must not** be removed and existing
260-
metadata field keys cannot be modified.
261267

262-
If the error message does *not* contain ErrorInfo payload (usually for
263-
pre-existing APIs):
268+
Even if an RPC has always returned `ErrorInfo`, the API **may** keep
269+
the existing `Status.message` stable and add a
270+
[`LocalizedMessage`](#localizedmessage) within `Status.details`.
264271

265-
- The `LocalizedMessage.message` *string* can change
266-
- However, the `Status.message` *string* **must** not be changed, as
267-
this change is backward-incompatible.
268-
269-
##### Message alignment
270-
271-
`LocalizedMessage` is populated with the same message as
272-
`Status.message`. Should you choose, you can also present a different
273-
`LocalizedMessage.message` from `Status.message`. For reasons why or why not,
274-
see [the rationale](#LocalizedMessage).
275-
276-
Keeping the aforementioned guidance in mind, the safest and recommended
277-
method to update an error message for a service is to add
278-
[`google.rpc.LocalizedMessage`][LocalizedMessage] to
279-
[`Status.details`][Status details]. `LocalizedMessage` is meant for
280-
displaying messages to end users.
281-
282-
Add as much information as the consumer of the error needs to resolve
283-
the error, but succinctly.
284-
285-
When including `LocalizedMessage`, both `locale` and `message` **must**
286-
be populated. If the service is to be localized, the value of `locale`
287-
**must** change dynamically. See "[Localization](#localization)".
288-
Otherwise, `locale` **must** always present the service's default
289-
locale; for example, "en-US".
290-
291-
When adding an error message using `LocalizedMessage`, `ErrorInfo`
292-
**must** be introduced either before or at the same time. If there are
293-
dynamic segments found in the text, the values of these variables
294-
**must** be included in `ErrorInfo.metadata`. See, "[Dynamic
295-
variables](#dynamic-variables)".
296-
297-
**Warning:** If `LocalizedMessage` is released without `ErrorInfo`, the
298-
same concerns regarding changing the value of the `message` field of
299-
`LocalizedMissage` apply: clients may misuse this textual error
300-
message--matching on string content.
272+
The content of `LocalizedMessage.details` **may** change over time.
301273

302274
### Partial errors
303275

@@ -445,16 +417,24 @@ differs from `Status.message` include the following:
445417

446418
### Updating Status.message
447419

448-
**Because Status.message is part of the API contract**, avoid updating
449-
it entirely in favor of updating LocalizedMessage; for example, clients
450-
often perform string matches on the text to differentiate one error for
451-
another and even parse the error message to extract variables from
452-
dynamic segments.
453-
454-
Clients **must** only perform string matches if their only option for
455-
extracting dynamic information is the message itself. If structured data
456-
is present in metadata, the recommended practice is to key into that
457-
data instead.
420+
If a client has ever observed an error with `Status.message` populated
421+
(which it always will be) but without `ErrorInfo`, the developer of that client
422+
may well have had to resort to parsing `Status.message` in order to find out
423+
information beyond just what `Status.code` conveys. That information may be
424+
found by matching specific text (e.g. "Connection closed with unknown cause")
425+
or by parsing the message to find out metadata values (e.g. a region with
426+
insufficient resources). At that point, `Status.message` is implicitly part
427+
of the API contract, so **must not** be updated - that would be a breaking
428+
change. This is one reason for introducing `LocalizedMessage` into the
429+
`Status.details`.
430+
431+
RPCs which have **always** included `ErrorInfo` are in a better position:
432+
the contract is then more about the stability of `ErrorInfo` for any given
433+
error: the reason and domain need to be consistent over time, and the
434+
metadata provided for any given (reason,domain) can only be expanded.
435+
It's still possible that clients could be parsing `Status.message` instead of
436+
using `ErrorInfo`, but they will always have had a more robust option
437+
available to them.
458438

459439
## Further reading
460440

0 commit comments

Comments
 (0)