Skip to content

Unbreak tutorials CI: pin katex + fastapi, keep Cayley init on SO(3) - #3961

Merged
atalman merged 3 commits into
mainfrom
pin-fastapi-serve
Sep 3, 2026
Merged

Unbreak tutorials CI: pin katex + fastapi, keep Cayley init on SO(3)#3961
atalman merged 3 commits into
mainfrom
pin-fastapi-serve

Conversation

@atalman

@atalman atalman commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Three fixes for the tutorials CI. Together with #3960 (merged) these clear every failure currently blocking #3959.

1. Docker image build -- pin katex to 0.18.4

Any PR that touches the docker context rebuilds the image, and the rebuild fails:

error commander@15.0.0: The engine "node" is incompatible with this module.
      Expected version ">=22.12.0". Got "20.20.2"
error Found incompatible module.

install_docs_reqs.sh installs Node 20 and then runs an unpinned yarn global add katex:

katex published commander
0.18.4 2026-08-10 ^8.3.0
0.18.5 2026-08-31 ^15.0.0

commander@15.0.0 declares engines: { node: ">=22.12.0" } and yarn enforces engines strictly. Pinned to katex@0.18.4, the last release on commander@^8.

This is the same commit as in #3959 -- it is needed here too, because fix 2 below edits requirements.txt and so triggers a rebuild. The longer-term fix is bumping the image from setup_20.x to setup_22.x; worth a separate PR so the Node bump gets reviewed on its own blast radius.

2. serving_tutorial.py -- pin fastapi below 0.139.2

File "beginner_source/serving_tutorial.py", line 116, in <module>
    class MNISTClassifier:
  ray/serve/api.py:377  pickle_dumps(app, error_msg="Failed to serialize the ASGI app.")
TypeError: Failed to serialize the ASGI app.:
  !!! FAIL serialization: cannot pickle '_thread.lock' object

fastapi 0.139.2 (2026-07-16) landed "Refactor router route building to make it thread-safe", adding to fastapi/routing.py:

_effective_routes_lock: Any = field(
    default_factory=threading.Lock, repr=False, compare=False
)

@serve.ingress(app) cloudpickles the FastAPI app, and a threading.Lock is not picklable. ray declares fastapi with no upper bound (fastapi; extra == "serve"), and requirements.txt left it unpinned, so any image rebuild picks it up. It does not reproduce on main today only because that image was last built 2026-07-08, before the release.

Bisected in a venv with ray[serve]==2.55.0, reproducing the tutorial's @serve.deployment / @serve.ingress / @serve.batch structure:

fastapi starlette result
0.139.0 0.50.0 OK
0.139.1 0.50.0 OK
0.139.2 0.50.0 FAIL
0.140.0 0.50.0 FAIL
0.141.1 1.6.0 FAIL
0.139.1 1.6.0 OK

The last row matters: starlette 1.6.0 is fine with fastapi 0.139.1, so despite the concurrent starlette 0.x -> 1.x major bump, starlette is not implicated and needs no pin.

A stopgap -- the real fix is upstream in ray, either excluding the lock from the pickled state or not pickling the app. Revisit when ray supports fastapi >= 0.139.2.

3. parametrizations.py -- keep the Cayley initialization on SO(3)

File "intermediate_source/parametrizations.py", line 325, in <module>
    layer_orthogonal.weight = nn.init.orthogonal_(layer_orthogonal.weight)
  parametrizations.py:310  return torch.linalg.solve(A + self.Id, self.Id - A)
torch._C._LinAlgError: torch.linalg.solve: The solver failed because the input matrix is singular.

nn.init.orthogonal_ samples from O(3), not SO(3). For an odd dimension, det(A) = -1 forces an eigenvalue at -1, so A + I is exactly singular -- and that is the matrix CayleyMap.right_inverse inverts. The block a few lines above already corrects the determinant; the "more succinctly" rewrite dropped it. This applies the same correction there, and says why inline.

200 trials with distinct seeds on torch 2.14.0+cpu, reproducing the tutorial's Skew and CayleyMap verbatim:

succeeded
current 185 / 200
with the determinant correction 200 / 200

No seed is set anywhere in the file, which is why this surfaces as a flaky failure rather than a consistent one.

AI assistance (Claude) was used for this change.

nn.init.orthogonal_ samples from O(3), so det(A) is -1 about half the time.
For odd dimensions that forces an eigenvalue at -1, making the A + I that
CayleyMap.right_inverse inverts singular:

    torch._C._LinAlgError: torch.linalg.solve: The solver failed because the
    input matrix is singular.

The block a few lines above already corrects the determinant; the succinct
rewrite dropped it. Apply the same correction there.

Signed-off-by: Andrey Talman <atalman@users.noreply.github.com>
serving_tutorial.py fails when the docker image is rebuilt:

    TypeError: Failed to serialize the ASGI app.:
    !!! FAIL serialization: cannot pickle '_thread.lock' object

fastapi 0.139.2 refactored router route building to be thread-safe, adding
_effective_routes_lock (a threading.Lock) to APIRouter. ray[serve]'s
@serve.ingress cloudpickles the FastAPI app, and a lock cannot be pickled.

ray declares fastapi with no upper bound, and requirements.txt left it
unpinned, so any image rebuild picks up the break.

Signed-off-by: Andrey Talman <atalman@users.noreply.github.com>
@pytorch-bot

pytorch-bot Bot commented Sep 2, 2026

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/tutorials/3961

Note: Links to docs will display an error until the docs builds have been completed.

⏳ No Failures, 1 Pending

As of commit 1d2ad9c with merge base 212f15f (image):
💚 Looks good so far! There are no failures yet. 💚

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-cla meta-cla Bot added the cla signed label Sep 2, 2026
@atalman atalman changed the title Pin fastapi below 0.139.2 so ray serve can pickle the ASGI app Unbreak serving_tutorial (pin fastapi) and parametrizations (keep Cayley init on SO(3)) Sep 2, 2026
@atalman atalman changed the title Unbreak serving_tutorial (pin fastapi) and parametrizations (keep Cayley init on SO(3)) Unbreak tutorials CI: pin katex + fastapi, keep Cayley init on SO(3) Sep 2, 2026

@huydhn huydhn left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dep changes look good to me! I don't have the context on intermediate_source/parametrizations.py though, so maybe we need another approval.

@atalman
atalman merged commit 982525e into main Sep 3, 2026
26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants