Skip to content

requested() throws when a command is called from server code (e.g. +server.ts) since 2.65.0 / #15991, unlike query.refresh() which no-ops #17035

Description

@alexbjorlig

Describe the bug

Since #15991 (shipped in 2.65.0), calling a command() from server code that is not a /_app/remote/… request (for example a +server.ts route, or a cron endpoint) throws as soon as the command calls requested():

Error: requested(...) can only be called in the context of a command/form remote function

Up to 2.64.0 this worked. The behaviour change is not mentioned in the PR, the changeset (fix: dedupe remote data, patch) or the changelog, and it is inconsistent with query.refresh(), which silently no-ops in exactly the same situation.

What changed in #15991 (commit 372a6a69):

  • app/server/remote/command.js no longer does state.remote.refreshes ??= new Map() when the command function runs.
  • app/server/remote/requested.js replaced if (!store) throw … with if (!state.is_in_remote_form_or_command) throw ….
  • server/respond.js initialises is_in_remote_form_or_command: false for every request.
  • server/remote.js is the only place that sets it to true, inside the form / command branches of the remote-function HTTP handler and the no-JS form POST handler.

So "inside a command" used to mean "the command wrapper is running" and now means "the request came through the remote-function endpoint". A command invoked from any other server entry point reads as "not in a command". The comment above the check in requested.js still says the maps "will be initialized by the command/form wrapper", which is no longer what happens.

Why it's inconsistent: refresh() in app/server/remote/query.js handles the same case with

if (!event.isRemoteRequest) {
    // or this is a no-JS form submission
    return;
}

i.e. a no-op. requested() throws. If calling commands outside remote requests is unsupported, I'd expect both to throw (and the docs to say so). If it is supported, I'd expect requested() to no-op like refresh(), or the command() wrapper to set the flag as it used to set the map.

Our case: we expose selected commands over a public JSON RPC route (/api/rpc/[procedure]/+server.ts) that validates input and calls the same command() functions the UI uses. Every command that does requested(someQuery, 1).refreshAll() started returning 500 after upgrading 2.61 → 2.70. We've worked around it with a wrapper that no-ops when getRequestEvent().isRemoteRequest is false, mirroring refresh().

Suggested resolutions (any one would do):

  1. requested() returns an empty result outside remote requests, matching refresh(); or
  2. the command() / form() wrappers set is_in_remote_form_or_command themselves (restoring the pre-2.65 semantics); or
  3. document that commands must not be called from non-remote server code, and make refresh() throw too so the rule is consistent.

Reproduction

Minimal, three files (I can push a StackBlitz/repo if that's preferred):

// src/lib/data.remote.ts
import { query, command, requested } from '$app/server';

export const getItems = query(async () => ['a', 'b']);

export const addItem = command(async () => {
    // ... write something ...
    await requested(getItems, 1).refreshAll();
    return { ok: true };
});
// src/routes/api/add/+server.ts
import { json } from '@sveltejs/kit';
import { addItem } from '$lib/data.remote';

export const POST = async () => json(await addItem());
<!-- src/routes/+page.svelte -->
<script>
    import { addItem } from '$lib/data.remote';
</script>
<button onclick={() => addItem()}>via remote endpoint (works)</button>
<button onclick={() => fetch('/api/add', { method: 'POST' })}>via +server.ts (500 since 2.67)</button>
  • kit 2.64.0: both buttons succeed.
  • kit ≥ 2.65.0: the second button returns 500 with the error above.

Logs

Error: requested(...) can only be called in the context of a command/form remote function
    at requested (node_modules/@sveltejs/kit/src/runtime/app/server/remote/requested.js:131:9)
    at addItem (src/lib/data.remote.ts)
    at POST (src/routes/api/add/+server.ts)

System Info

Node: 24.13.0
@sveltejs/kit: 2.70.3 (bisected to 2.65.0 / #15991 by diffing the published tarballs 2.64.0 → 2.65.0)
svelte: 5.56.6
vite: 8.2.2
OS: macOS 25.6 (also reproduced on Linux CI)

Severity

annoyance (there is a workaround, but it's an unannounced behaviour change in a patch release)

Additional Information

Related: #16129 reworded the requested docs to "inside a remote command or form callback" shortly after, but a command called from +server.ts is inside the command callback, so the docs don't currently distinguish the two cases either.

Edit: corrected the version the change shipped in. The changelog lists #15991 under both 2.65.0 and 2.67.0; the published tarballs show the guard first appears in 2.65.0.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions