@@ -31,6 +31,9 @@ to only the properties that are relevant to that hook. This makes each hook
3131easier to reason about, and helps to ensure that a change in execution order
3232doesn't result in different behavior.
3333
34+ Hooks are intended to be lightweight, so blocking operations should not be
35+ supported.
36+
3437### Hook sequence
3538
3639The following is an ordered list of recommended hooks. It is also recommended to
@@ -86,24 +89,28 @@ protocols, these are HTTP requests and HTTP responses.
86898 . ** readAfterExecution** * (immutable)* — The last thing called during an
8790 execution.
8891
89- ### Error behavior
92+ ### Error handling
93+
94+ Error handling behavior depends on where in the pipeline the hook is called and
95+ whether or not the hook is mutable.
96+
97+ #### Error accumulation
9098
91- Errors raised in hooks are handled consistently. The behavior depends on where
92- in the pipeline the hook is called:
99+ When an immutable hook is being executed, all interceptors are always invoked
100+ and any errors thrown by those interceptors are accumulated before any further
101+ action is taken. The last error thrown by an interceptor is re-thrown. If the
102+ language supports suppressed errors, the other errors should be suppressed. If
103+ not, the other errors should be logged and dropped. The may also be attached to
104+ the raised error as metadata.
93105
94- - ** Hooks called once per execution** (` readBeforeExecution ` ,
95- ` readAfterExecution ` ): Errors are collected across all interceptors before any
96- further action is taken. If multiple interceptors raise errors, the last error
97- wins and earlier ones are logged and dropped.
106+ When a mutable hook is being executed, the first error thrown is immediately
107+ forwarded on and no other interceptors are invoked.
98108
99- - ** Most other hooks** : An error immediately jumps execution to
100- ` modifyBeforeAttemptCompletion ` (if inside the retry loop) or
101- ` modifyBeforeCompletion ` (if outside), with the error set as the result.
109+ #### Control flow
102110
103- - ** ` readAfterAttempt ` ** : Errors are collected the same way as
104- ` readBeforeExecution ` . After all interceptors have been called, the
105- [ retry strategy] ( #client-guidance-retries ) decides whether to retry or proceed
106- to ` modifyBeforeCompletion ` .
111+ When an error is raised in a hook outside the retry loop, execution immediately
112+ jumps to ` modifyBeforeCompletion ` . When an error is raised outside the retry
113+ loop, execution immediately jumps to ` modifyBeforeAttemptCompletion ` .
107114
108115## Interfaces
109116
@@ -138,7 +145,22 @@ public class ResponseHook<I, O, RequestT, ResponseT> extends RequestHook<I, O, R
138145// Available from readAfterDeserialization and modifyBeforeAttemptCompletion onward.
139146// Adds the deserialized output (may be null if the attempt failed).
140147public class OutputHook <I, O, RequestT, ResponseT> extends ResponseHook<I , O , RequestT , ResponseT > {
141- public O output () { ... }
148+ public Optional<O > output () { ... }
149+
150+ public Optional<RuntimeException > error () { ... }
151+
152+ /**
153+ * If an exception {@code e } is provided, throw it, otherwise return the output value.
154+ *
155+ * @param e Error to potentially rethrow.
156+ * @return the output value.
157+ */
158+ public O forward (RuntimeException e ) {
159+ if (e != null ) {
160+ throw e;
161+ }
162+ return output;
163+ }
142164}
143165```
144166
@@ -213,6 +235,19 @@ public interface Interceptor {
213235}
214236```
215237
238+ :::{admonition} Error modeling
239+ :class: important
240+
241+ In these interfaces, errors are provided separately from the ` OutputHook ` type.
242+ This is a minor optimization that eliminates the need to allocate new ` OutputHook `
243+ instances when errors are thrown.
244+
245+ In languages that have a native union type (such as Python) or a native ` Result `
246+ type (such as Rust), the usability benefits of integrating the error into the
247+ ` OutputHook ` may be preferrable.
248+
249+ :::
250+
216251## Example
217252
218253The following interceptor adds a tracing header to HTTP requests when running
@@ -222,7 +257,7 @@ cause the signature to include the header. That would be fine, but this
222257particular header is added after signing in practice.
223258
224259``` java
225- public class AddTraceHeader implements ClientInterceptor {
260+ public class AddTraceHeader implements Interceptor {
226261 private final String traceId;
227262
228263 public AddTraceHeader (String traceId ) {
0 commit comments