Server pages load twice on the server (`?pick=default` and the plain file), so their data GETs return 405
Maintainers usually reply within 1 day
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 35/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- typescript, vite
- Domain
- backend-api-design, build-system
Research direction
Look at the file-routing plugin's manifest generation in the SSR environment, specifically how it imports server pages with ?pick=default. Examine the interaction with @solidjs/vite-plugin's server-function handler, which imports the plain file. The fix likely involves modifying the import logic in filesystem-routing or coordinating with the Vite plugin to avoid duplicate module instances. Start by understanding the module resolution in vite.config.ts and the resulting server bundle.
Written by the indexing model from the issue text.
Description
Describe the bug
With fileRoutes({ serverComponents: true }), a server page's route file is imported twice in the SSR environment:
- The manifest imports it as
src/routes/x.tsx?pick=default, which is the eager ref forserver: truepages. @solidjs/vite-plugin's server-function handler imports the plainsrc/routes/x.tsx, because the file contains"use server".
These are two module instances, and both run registerServerReference("<id>", fn) with the same id. In the production server bundle it shows up as two registrations:
var serverFunction_1$6 = registerServerReference("StoriesLayout-3c228249", async function StoriesLayout(_args) { ... // from ?pick=default
var serverFunction_1$1 = registerServerReference("StoriesLayout-3c228249", async function StoriesLayout(_args) { ... // from the handler's import
The second registration replaces the first in @solidjs/web's registry. @solidjs/router's fs adapter wraps the page in query(), which declares the function GET through GET(reference). That grant is bound to the first registration. declaresRead(id) checks the grant against the current registration, so it's false, and every client-side call to the page fails:
GET /_server/data/StoriesLayout-3c228249?args=... -> 405 Method Not Allowed (Allow: POST)
Server rendering works, since it calls the function directly. Client navigation to any server page returns 405, in both vite dev and the production build.
Your Example Website or App
I reproduced this in a small stories app (one server layout and two server pages), in both dev and build, by removing the workaround plugin. The setup below is the same configuration cut down to one page. I haven't run that cut-down version on its own.
Steps to Reproduce the Bug or Issue
Versions: [email protected], @solidjs/[email protected], @solidjs/[email protected], @solidjs/[email protected], [email protected].
vite.config.ts:
plugins: [
solid({ start: {}, ssr: true, serverFunctions: { components: true } }),
fileRoutes({ serverComponents: true })
]
src/router.ts:
import { pageRoutes } from "virtual:file-routes";
import { createRouter } from "@solidjs/router";
import { fileRoutes } from "@solidjs/router/fs";
export const Router = createRouter({ routes: fileRoutes(pageRoutes) });
src/routes/index.tsx is a client page with <a href="/about">About</a>. src/routes/about.tsx is a server page:
export default async function About() {
"use server";
return () => <p>About</p>;
}
(src/app.tsx renders <Router>. It also calls enableRichArguments(), to get past a separate router bug where search: undefined fails JSON argument encoding.)
- Run
vite buildand look indist/server/server.js.registerServerReference("About-...")appears twice. - Start the server and load
/, then click "About". GET /_server/data/About-...returns 405. The same happens undervitedev.
Expected behavior
Each server page should have exactly one module instance on the server, so the function the router declares GET is the one the handler dispatches to. Some possible fixes:
- filesystem-routing: in server environments, give server-page refs the plain module id instead of
?pick=default. Picking gains nothing for a module the handler loads in full anyway. @solidjs/vite-plugin: have the handler manifest reuse the?pick=id for modules the file-routes manifest already imports.@solidjs/web: make a secondregisterServerReferencefor an id that already has a different function an error, or at least a dev warning. Today it silently replaces the first, which is why this surfaces as an unexplained 405.
A local workaround that fixes both dev and build is a Vite plugin, limited to the ssr environment, that strips ?pick= from src/routes/ imports in resolveId (order: "pre"). The client keeps its picked, code-split modules.
Platform
- OS: macOS 15 (Darwin 24.3.0)
- Browser: Chromium 149 (headless, Playwright)
- Versions: as above, Node 24.21.0
- Dominant language
- TypeScript
- Stars
- 20
- Forks
- 5
- Avg merge
- 5d 22h
- Merged PRs (30d)
- 8
Getting set up
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from solidjs/filesystem-routing
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
solidjs/filesystem-routing#3 ·
Maintainers usually reply within 1 day
-
Difficulty 5/5 Over a week Newbie friendliness 25/100
solidjs/filesystem-routing#26 ·
Maintainers usually reply within 1 day
-
Difficulty 5/5 Over a week Newbie friendliness 25/100
solidjs/filesystem-routing#25 ·
Maintainers usually reply within 1 day
-
Generate route declarations without starting VitePossibly taken @everton-dgn claimed this 16 days ago. Open
Difficulty 3/5 Half a day Newbie friendliness 28/100
solidjs/filesystem-routing#23 ·
Maintainers usually reply within 1 day
All issues in solidjs/filesystem-routing
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
marmelab/react-admin#11431 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
db-ux-design-system/core-web#8655 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
stripe/react-stripe-js#728 ·
-
[Upload Grade C dataset] the pop-up changes it's size while uploading the document => Jumping effectOpenbug
Difficulty 2/5 1-3 hours Newbie friendliness 66/100
epam/statgpt-admin-frontend#305 ·
Maintainers usually reply within 1 day