Skip to content

Remote functions: form.action throws Cannot access event.url in a query after await query() in WebContainer / StackBlitz #16818

Description

@janekprange

Describe the bug

On a WebContainer runtime (StackBlitz), a page that awaits a remote query() and then renders a remote form() in the same SSR pass throws:

Error: Cannot access event.url in a query. Pass the value as an argument to the query instead

No user code reads event.url. Kit's form.action getter does, to build the ?/remote=… action:

// packages/kit/src/runtime/app/server/remote/form.js
get: () => {
	const search = new URLSearchParams(get_request_store().event.url.search); // <- throws
	...
}

That read is guarded to throw while a query is running (is_in_remote_query):

// packages/kit/src/runtime/app/server/remote/shared.js
if (state.is_in_remote_query) {
  for (const property of ["url", "params", "route"]) {
    Object.defineProperty(derived, property, {
      get() {
        throw new Error(
          `Cannot access event.${property} in a query. Pass the value as an argument to the query instead`,
        );
      },
    });
  }
}

Normally the query's request store is scoped to the query callback and cleared afterward, so the later form.action read sees the page's store (is_in_remote_query === false). In a WebContainer the ambient store is a module global that is never reset on purpose:

// packages/kit/src/exports/internal/server/event.js
export function with_request_store(store, fn) {
  try {
    sync_store = store;
    return als ? als.run(store, fn) : fn();
  } finally {
    // Since AsyncLocalStorage is not working in webcontainers, we don't reset `sync_store`
    // and handle only one request at a time in `src/runtime/server/index.js`.
    if (!IN_WEBCONTAINER) {
      sync_store = null;
    }
  }
}
// IN_WEBCONTAINER = !!globalThis.process?.versions?.webcontainer

After await getData() returns, sync_store still points at the query's store (is_in_remote_query === true) and is never restored. When the component then renders <form {...myForm}>, form.actionget_request_store().event.url hits that leftover query store and throws. Under export const csr = false this happens during pure SSR, so the page 500s.

On a runtime with real AsyncLocalStorage (normal Node) the finally resets sync_store and ALS scopes the store to the query callback. Same code renders fine there. This only reproduces in WebContainer / StackBlitz as far as I can see.

Reproduction

Minimal case: a .remote.ts with a query and a form, plus a page that awaits the query then renders the form.

// src/routes/data.remote.ts
import { query, form } from "$app/server";

export const getData = query(() => ({ title: "Ada" }));
export const save = form("unchecked", async () => {});
<!-- src/routes/+page.svelte -->
<script lang="ts">
	import { getData, save } from './data.remote';
	const data = await getData(); // top-level await (Svelte experimental.async)
</script>

<form {...save}>
	<!-- rendering the form reads form.action -> event.url -->
	<input {...save.fields.title.as('text', data.title)} />
	<button>Save</button>
</form>

Needs experimental.async (Svelte compiler) and experimental.remoteFunctions (Kit). Add export const csr = false; in +page.ts to force pure SSR, then it 500s every time on StackBlitz. On local Node it returns 200.

Existing repro that hits this: any no-js (csr = false) route in
https://github.com/janekprange/sveltekit-remote-form-post-success-rehydrate
500s on StackBlitz (500 GET /default/no-js) but works locally on Node.

Logs

Error: Cannot access event.url in a query. Pass the value as an argument to the query instead
    at eval (src/lib/SettingsForm.svelte:122:67)
500 GET /default/no-js

System Info

Binaries:
  Node: 25.2.1
  pnpm: 10.31.0
npmPackages:
  @sveltejs/adapter-auto: next => 8.0.0-next.3
  @sveltejs/kit: next => 3.0.0-next.23
  @sveltejs/vite-plugin-svelte: ^7.2.0 => 7.3.0
  svelte: ^5.56.7 => 5.56.9
  vite: ^8.1.5 => 8.2.1


Only reproduces on WebContainer runtimes (StackBlitz). Same code is fine on a normal Node runtime (dev and production build).

Severity

serious, but I can work around it

Additional Information

Related:

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions