Fix HTTP codegen infinite loop for [AsParameters] + [ReadAggregate]/[WriteAggregate]#3125
Merged
Merged
Conversation
…Aggregate] hybrid endpoints A hybrid HTTP endpoint that binds a parameter object with [AsParameters] (carrying the aggregate id via a [FromRoute] member) and also loads the aggregate with [ReadAggregate]/[WriteAggregate] sent HTTP code generation into an infinite loop (StackOverflow): AsParametersBindingFrame -> ... -> ReadHttpFrame -> AsParametersBindingFrame -> ... The route-read frame that AsParametersBindingFrame owns and generates inline (as a constructor argument of the bound object) was ALSO pulled into the method's frame chain, because the aggregate-id resolution (ValueSource.Anything) resolved the id to that same route Variable. The arranger then wired the owned frame a Next back into the chain, and generating it inline re-entered the whole chain forever. Fix: when an endpoint binds an [AsParameters] object, a ValueSource.Anything lookup for a value that lives on that object now resolves to a member access on the bound object (e.g. query.Id) instead of the owned route/query read frame. The aggregate loader then depends on the AsParameters object (a normal standalone frame) rather than on the inline-generated read frame, so there is no cyclic Next. Scoped to ValueSource.Anything so specific-source bindings are unaffected. Adds an end-to-end test (route id through [AsParameters] + [ReadAggregate]) plus the sample endpoint. Full Wolverine.Http.Tests suite green (779 passing). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced Jun 17, 2026
Merged
This was referenced Jun 26, 2026
This was referenced Jul 5, 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.
Problem
A hybrid message-handler / HTTP endpoint that uses Marten with
[ReadAggregate](or[WriteAggregate]), brings the aggregate identifier through a route argument, and binds the request with[AsParameters]sends HTTP code generation into an infinite loop (StackOverflow), so the app can't boot.Reproduction (the reported shape):
Root cause (all in code generation)
The cyclic
Nextchain that overflows the stack:AsParametersBindingFrameowns the route-readReadHttpFramefor the id and generates it inline (as a constructor argument of the boundqueryobject). The[ReadAggregate]/[WriteAggregate]identity resolution searches withValueSource.Anything, andHttpChain.TryFindVariablereturned that same owned routeVariable. The aggregate-loader frame therefore depended on it, soMethodFrameArrangerpulled the ownedReadHttpFrameinto the method chain and gave it aNext— and generating it inline re-entered the whole chain, forever.(This only happens with
[AsParameters]: a plain route param or[FromQuery]id binds to a standalone frame that nothing re-embeds, so there's no cycle — which is why the existing/orders/latest/{id}and…/from-queryendpoints work.)Fix
When an endpoint binds an
[AsParameters]object, aValueSource.Anythinglookup for a value that lives on that object now resolves to a member access on the bound object (query.Id) instead of the route/query read frame thatAsParametersBindingFrameowns and generates inline. The aggregate loader then depends on the[AsParameters]object (a normal standalone frame in the chain) rather than on the inline-generated read frame — no cyclicNext.HttpChain.AsParametersVariable, set by the[AsParameters]strategy.HttpChain.TryFindVariableshort-circuits tonew MemberAccessVariable(AsParametersVariable, member)for matching members, scoped toValueSource.Anythingso specific-source (RouteValue/FromQuery/Header) bindings are untouched.Generated code is now straightforward and correct:
Tests
happy_path_reading_aggregate_with_id_from_asparameters_routeexercising the route-id-through-[AsParameters]+[ReadAggregate]combination (would hang on regression), plus the sample endpoint inWolverineWebApi.Wolverine.Http.Testssuite run locally: 779 passed, 10 skipped, 0 failed — no regressions in the regression-prone HTTP codegen.🤖 Generated with Claude Code