fix(server): propagate render context to sync handlers in executor#38
Merged
Az107 merged 1 commit intoJul 12, 2026
Merged
Conversation
run_in_executor does not carry contextvars into the worker thread, so the render context set by init_render_ctx was invisible to sync route handlers: use_state() and @js registered nothing and pages shipped without their <script> blocks or state initializers, silently. Running the handler through contextvars.copy_context() makes the executor thread see the same RenderContext object, whose mutations the main thread then renders. Async handlers are unaffected.
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
use_state()and@jsare silently broken inside sync route handlers.The response ships an empty
<head />— noLocalStateinitializer, no transpiled function — so the page shows a raw{{localstate_…}}placeholder andonclickcalls an undefined function. The same handler declaredasync defworks.Cause
handle_requeststores the render context in aContextVar(init_render_ctx) and then runs sync handlers vialoop.run_in_executor(...).run_in_executordoes not propagatecontextvars, so inside the worker threadget_render_ctx()returnsNoneand every registration is dropped without error.Fix
Run the handler through
contextvars.copy_context():The copied context sees the same
RenderContextobject, so registrations made in the executor thread are visible when the main thread renders the response. Async handlers are unaffected.Verification
Reproduced on
develop(60e7e1f) with uvicorn + curl: before the patch the sync handler renders<head />; after it, both<script>blocks (state initializer + transpiledincrement) are injected and identical to the async handler's output.ruff checkpasses.