Fix build failure when frontend_path is set without prerendering - #7044
Conversation
… output When frontend_path is set, build() moves every child of .web/build/client into .web/build/client/<frontend_path>/. With route prerendering enabled, React Router already emits the prerendered HTML under that prefix, so the target directory exists by accident. With prerendering disabled (REFLEX_SSR=false) nothing creates it: os.rename fails, shutil.move falls back to copy2, and opening the destination raises FileNotFoundError, crashing `reflex run --env prod` and `reflex export`. Create the prefix directory up front so the relocation no longer depends on prerendering. The existing skip for the prefix's first segment keeps the prerendered output merging in as before. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UwbNDEaQNGWeHWS8nxsURd
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UwbNDEaQNGWeHWS8nxsURd
Merging this PR will improve performance by 3.18%
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ⚡ | test_var_access[mutable_dict] |
47 ms | 45.5 ms | +3.18% |
Tip
Curious why performance improved? Comment @codspeedbot explain why performance improved on this PR, or directly use the CodSpeed MCP with your agent.
Comparing claude/happy-brown-ajf67r (a0480cc) with main (5d9724e)
Footnotes
-
8 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩
There was a problem hiding this comment.
All reported issues were addressed across 3 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
frontend_path is a URL prefix, but it also names the directory below .web/build/client that the production build is relocated into (build.py) and served from (exec.py). A ".." segment would move the built frontend outside the build output, and since path_ops.mv removes existing destinations, could overwrite neighboring files. Validate this once in Config._normalize_paths so every consumer can trust the value, rather than re-checking at each filesystem use. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UwbNDEaQNGWeHWS8nxsURd
…ndows PosixPath cannot be instantiated on Windows, so the prefix relocation in build() raised UnsupportedOperation there for any app with frontend_path set. The value is only used for its parts and to join onto static_dir, which PurePosixPath supports on every platform. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UwbNDEaQNGWeHWS8nxsURd
The previous guard only rejected ".." between forward slashes. On Windows a backslash is also a separator, so "..\\escaped" still traversed out of the build output, and a drive-letter or rooted segment such as "D:other" or "\\" makes pathlib replace the base path entirely. Validate each slash-delimited segment with PureWindowsPath: it must have no anchor (drive or root) and parse to exactly itself, which rules out backslashes, drive prefixes, UNC forms, and ".". Bare ".." is rejected explicitly since it parses to itself. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UwbNDEaQNGWeHWS8nxsURd
Builds the frontend_path + REFLEX_SSR=false combination the changelog names. Both reflex export and reflex run --env prod fail on 0.9.10.post2 with exactly the documented FileNotFoundError and succeed on 0.9.11a1, and the resulting prod deployment works end to end in a browser under the sub-path — state round-trip, client-side routing, reload and deep link. Probing the same deployment with curl shows every route but / is answered with HTTP 404 while serving the correct SPA document (FINDING-037). A four-way matrix shows the trigger is REFLEX_SSR=false rather than frontend_path, and that the previous stable does the same, so it is pre-existing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0167TFRX1CXqwGK8uh7DBE7c
Type of change
Description
Fixes a
FileNotFoundErrorthat occurs when runningreflex run --env prodorreflex exportwithfrontend_pathconfigured and route prerendering disabled (REFLEX_SSR=false).Root cause: When prerendering is disabled, the static output directory structure is not created with the
frontend_pathprefix. The build process attempted to move files into a non-existent prefix directory, causing the failure.Solution: Explicitly create the prefix directory before attempting to relocate static files into it. This ensures the directory exists regardless of whether prerendering created it.
Changes
reflex/utils/build.py: Modified thebuild()function to create the prefix directory before moving files, and simplified the file movement logic to use the pre-computedprefix_dirvariable.tests/units/utils/test_build.py:_patch_build()helper to reduce duplication_write_static_output()helpertest_build_relocates_static_output_without_prerendered_prefix_dir()to verify files are correctly relocated when prerendering is offtest_build_merges_static_output_into_prerendered_prefix_dir()to verify prerendered output is preserved when merging with non-prerendered assetsnews/+frontend-path-noprerender-build.bugfix.md: Added changelog entry documenting the fix.Test Plan
Added comprehensive unit tests covering both scenarios:
All existing tests continue to pass.
https://claude.ai/code/session_01UwbNDEaQNGWeHWS8nxsURd