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
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 20.8k
- Forks
- 2.3k
- Avg merge
- 1d 16h
- Merged PRs (30d)
- 156
Description
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.jsno longer doesstate.remote.refreshes ??= new Map()when the command function runs.app/server/remote/requested.jsreplacedif (!store) throw …withif (!state.is_in_remote_form_or_command) throw ….server/respond.jsinitialisesis_in_remote_form_or_command: falsefor every request.server/remote.jsis the only place that sets it totrue, inside theform/commandbranches 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):
requested()returns an empty result outside remote requests, matchingrefresh(); or- the
command()/form()wrappers setis_in_remote_form_or_commandthemselves (restoring the pre-2.65 semantics); or - 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.
Contributor 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.
Research direction
Start with app/server/remote/requested.js and compare its guard with app/server/remote/query.js, then trace the command/form paths in app/server/remote/command.js and server/remote.js. Reproduce the +server.ts case described in the issue and verify the chosen behavior is consistent for requested() and refresh() outside remote requests, including the documented cases.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, typescript
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100