Skip to content

handleFetch hook receive empty event object after 2.31.0 sveltekit upgrade #15040

Closed

Description

@paras-taneja

Describe the bug

handleFetch hook receive empty event object after 2.31.0 sveltekit upgrade.
For ssr http calls handleFetch act as an http interceptor and we add auth details via event object . But after the upgrade the ssr calls started to throw 401 as auth details were empty in the event object as event object itself was coming empty.

Reproduction

export const handleFetch = (async ({ event, request, fetch }) => {
	const { token } = event.locals;
	const cookieString = event.request.headers.get('cookie') || '';
	const headers = getDefaultHeaders(request.url, cookieString, token, sendAuthorisation);

	Object.entries(headers).forEach(([key, value]) => {
		request.headers.set(key, value);
	});

	const resource = Object.keys(PRIVATE_URL_MAPPING).find((resource) =>
		request.url.startsWith(resource)
	);
	if (!dev && resource && PRIVATE_URL_MAPPING[resource]) {
		request = new Request(request.url.replace(resource, PRIVATE_URL_MAPPING[resource]), request);
	}

	return fetch(request);
}) satisfies HandleFetch;

upgrade sveltekit version to 2.31.0 from 2.30.1, the issue will popup

Logs

System Info

System:
    OS: macOS 15.6.1
    CPU: (10) arm64 Apple M1 Pro
    Memory: 536.16 MB / 16.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 24.7.0 - /opt/homebrew/bin/node
    npm: 11.5.1 - /opt/homebrew/bin/npm
    pnpm: 10.21.0 - /opt/homebrew/bin/pnpm
  Browsers:
    Safari: 26.1
  npmPackages:
    @sveltejs/adapter-auto: ^6.0.1 => 6.1.1 
    @sveltejs/adapter-node: ^5.2.12 => 5.4.0 
    @sveltejs/kit: 2.31.0 => 2.31.0
    @sveltejs/vite-plugin-svelte: ^6.2.1 => 6.2.1 
    svelte: ^5.30.1 => 5.43.12 
    vite: ^7.2.2 => 7.2.2

Severity

annoyance

Additional Information

No response

Activity

  1. Conduitry commented on Dec 8, 2025

    @Conduitry
    Member

    Please provide a complete and minimal reproduction, not just a code snippet referring to other parts of your codebase that aren't included.

  2. timephy commented on Dec 17, 2025

    @timephy

    When updating past 2.31.0 I get this error with vite dev SSR using the provided fetch to request some data.

    CORS error: No 'Access-Control-Allow-Origin' header is present on the requested resource
        at universal_fetch (...)
    

    I run this in hooks.server.ts:

    const handleForwardedProto: Handle = async ({ event, resolve }) => {
        const proto = event.request.headers.get("x-forwarded-proto")
        if (proto) {
            event.url = new URL(event.url.href.replace(/^http:/, `${proto}:`))
        }
        return resolve(event)
    }
    export const handle = handleForwardedProto

    I set this up a long time ago.

    Removing it does not solve the issue.

  3. timephy commented on Dec 18, 2025

    @timephy

    CORS error: No 'Access-Control-Allow-Origin' header is present on the requested resource
    at universal_fetch (...)

    I should have mentioned that I have a Traefik Reverse Proxy on a dev.example.com domain, that proxies the request to my local machine running vite dev.

  4. golaughlin commented on Dec 25, 2025

    @golaughlin

    I'm also getting a CORS error: No 'Access-Control-Allow-Origin' header is present on the requested resource error when I try to fetch JSON from a REST API.

    ./routes/children/+page.ts

    import type { PageLoad } from "./$types";
    
    interface Child {
        id: number;
        firstName: string;
        lastName: string;
    }
    
    export const load: PageLoad = async ({ fetch }) => {
        const res = await fetch("http://localhost:8000/children");
        const fetchedChildren = await res.json();
    
        return {
            children: fetchedChildren.map((child: Child) => ({
                id: child.id,
                firstName: child.firstName,
                lastName: child.lastName
            }))
        };
    }
    

    ./routes/children/+page.svelte

    <script lang="ts">
        import type { PageProps } from "../$types";
    
        let { data }: PageProps = $props();
    </script>
    
    <h1 class="font-bold text-4xl mb-4">List of Children</h1>
    
    <ul class="">
        {#each data.children as { id, firstName, lastName }}
            <li><a href="/children/{id}">{lastName}, {firstName}</a></li>
        {/each}
    </ul>
    

    VSCode also throws a Property 'children' does not exist on type '{}' problem.

  5. timephy commented on Jan 7, 2026

    @timephy

    As this (in my case) seems to depend on individual-domain-hosting... How can I help recreate this for you? @Conduitry

    I would love to help push this into the right direction to get this fixed. It's preventing me from updating atm..

  6. timephy commented on Feb 14, 2026

    @timephy

    I, again, updated past SvelteKit 2.31.0 and ran into the same issue.

    However I found a fix. But I really don't know if this is the way it is supposed to be...

    // hooks.server.ts
    
    // We have to handle the x-forwarded-proto header, because we are deployed behind a reverse proxy
    // Otherwise CORS errors with sveltekit server to backend will occur, especially with using the `fetch` API in load function
    
    export const handle: Handle = async ({ event, resolve }) => {
        const proto = event.request.headers.get("x-forwarded-proto")
        if (proto) {
            event.url = new URL(event.url.href.replace(/^http:/, `${proto}:`))
        }
        return resolve(event)
    }
    
    export const handleFetch: HandleFetch = async ({ request, event, fetch }) => {
        const url = new URL(request.url)
        const proto = event.request.headers.get("x-forwarded-proto")
    
        // Derive public origin (same logic as handleForwardedProto)
        const publicOrigin = proto ? event.url.origin.replace(/^http:/, `${proto}:`) : event.url.origin
    
        // Inject Origin header for cross-origin requests
        if (url.origin !== publicOrigin && !request.headers.get("origin")) {
            const headers = new Headers(request.headers)
            headers.set("origin", publicOrigin)
            request = new Request(request, { headers })
        }
    
        return fetch(request)
    }

    My network setup is the following:

    Frontend: https://example.com → Traefik Reverse Proxy → SvelteKit/Vite dev server
    Backend: https://api.example.com → Traefik Reverse Proxy → Rust backend server


    Request for comment

    🙏🏽 I'd highly appreciate at least a comment pointing out a possible mistake or misunderstanding...

  7. teemingc commented on Feb 14, 2026

    @teemingc
    Member

    The issue with troubleshooting this is that there are too many variables to consider. The very least we would need is to know the exact way you’ve setup your reverse proxy, sveltekit project and the how the request was sent. So we need a reproduction repository and reproduction steps

  8. Nic-Polumeyv commented on Jul 26, 2026

    @Nic-Polumeyv
    Member

    https://github.com/Nic-Polumeyv/sveltekit-handlefetch-event-probe runs the same app against 2.30.1, 2.31.0 and 2.70.1. On 2.30.1, a handle hook that reassigns event.url from x-forwarded-proto propagates everywhere. From 2.31.0 on, handleFetch sees the stale url, and a reassigned event.locals arrives empty, which is the empty event reported here.

    The cause is #13899, shipped in 2.31.0. handle receives a spread copy of the event (traced_event = { ...event, tracing } in respond.js), while event.fetch was created earlier and closed over the original. Reassignments inside handle land on the copy. handleFetch, the origin header kit injects into server fetches, and cookie forwarding all read the original; the CORS check in universal load reads the copy. In the proxy setup no backend header can pass: kit sends Origin: http://… and when the backend echoes exactly that in Access-Control-Allow-Origin, the check still fails because it compares against the mutated url. Property writes like event.locals.user = x still propagate because the copy is shallow. @timephy's workaround works because kit only injects origin when the header is absent, so setting it in handleFetch wins.

    @golaughlin's case is different: fetching a cross-origin API that returns no Access-Control-Allow-Origin fails on every version, that check predates this (#7454).

    Rich suggested making tracing getter-based during #13899 review so the same event object could flow throughout (#13899 (comment)), which would fix this class. The alternative is declaring property reassignment in handle unsupported, as #11902 (comment) already does for the underlying x-forwarded-proto pattern, though 2.30.x supported it in practice.

  9. Rich-Harris commented on Aug 5, 2026

    @Rich-Harris
    Member

    You can't just reassign event.url. That needs to happen before SvelteKit sees the app. I guess we need to mark that property readonly in the types.

    I'm also getting a CORS error: No 'Access-Control-Allow-Origin' header is present on the requested resource error when I try to fetch JSON from a REST API.

    It sounds like whatever's serving from localhost:8000 isn't adding an Access-Control-Allow-Origin header? Seems unrelated to the issue at hand.

    VSCode also throws a Property 'children' does not exist on type '{}' problem.

    That's because you're importing ../$types instead of ./$types. If you're using VS Code with the Svelte extension you don't need to import any types, they will be injected for you.

    Am inclined to close this issue — the OP is vague and there's no repro, and the other two things don't sound like SvelteKit bugs.

  10. added a commit that references this issue on Aug 5, 2026
    8763327
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions