Repository navigation
Fix InterceptorChain modifications, error handling, and hook dispatch - #852
Merged
Merged
Conversation
InterceptorChain called every interceptor's modify_* hook with the original context and returned only the last interceptor's result. Any interceptor after one that modified the request, response, or output received the unmodified value, and returning it unchanged, as the base Interceptor does, discarded the earlier modification. For example, a user interceptor added through per-call plugins undid changes an operation's default interceptor made in modify_before_signing. Each interceptor now receives a context holding the value returned by the interceptor before it. modify_before_completion also called each interceptor's modify_before_attempt_completion hook instead of its modify_before_completion hook.
When an interceptor raised in a read_* hook, InterceptorChain let the exception propagate immediately, so the interceptors after it never saw that stage. Read hooks can't change the request or response, so one failing shouldn't keep the others from running. Each read hook now runs on every interceptor, then the last exception is raised and earlier ones are logged and dropped. read_after_execution already ran every interceptor and now logs the exceptions it drops too. modify_* hooks still stop at the first exception, since later interceptors would receive a value that was never produced.
The pipeline serialized and resolved endpoints from call.input, so the request returned by modify_before_serialization never reached the wire. It now uses the modified request, including on the serialization error path. InterceptorChain now raises TypeError when a modify_* hook returns None, passes errors to later interceptors in read_after_attempt as read_after_execution does, logs dropped errors at debug, and reuses the context when a hook returns its input unchanged. Read-hook docstrings now describe running every interceptor before raising.
kuhe
approved these changes
Oct 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The
InterceptorChaincurrently passes the original context to every interceptor, so amodify_*hook's result gets lost whenever another interceptor runs after it on the same hook. That includes the baseInterceptordefaults, which returned the original value. Additionally,modify_before_completionincorrectly calls each interceptor'smodify_before_attempt_completion. In 11 of the 12 read hooks, one raising interceptor kept every later interceptor from running. Separately,RequestPipelineserialized and resolved endpoints fromcall.input, so the request returned bymodify_before_serializationnever reached the wire.This PR passes each interceptor's result to the next interceptor and makes
modify_before_completioncall its own hook. Every read hook now runs all interceptors before raising the last error, as the Smithy interceptor guide specifies for immutable hooks. Earlier errors are logged at debug level, andread_after_attemptpasses each error on to later interceptors, asread_after_executionalready did. The pipeline now uses the modified request for serialization, endpoint resolution, and the serialization error path. Amodify_*hook that returnsNonenow raisesTypeErrorinstead of passingNoneto the next interceptor.Before / After
Before
After
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.