The optimizer rewrites const { a, ...rest } = obj inside a routeLoader$ into core's
_restProps(obj, ["a"]). _restProps only understands component-props proxies — it reads the
_VAR_PROPS / _CONST_PROPS symbols off its argument. For an ordinary object those symbols are
undefined, so it returns an empty props proxy. The rest object loses every key, server-side,
with no error.
@qwik.dev/core2.0.0-beta.38 (pristine, unpatched)@qwik.dev/router2.0.0-beta.38- vite 6.4.3, bun 1.2.17, node/darwin arm64
bun install
bun run dev # vite --mode ssr, port 5803The loader awaits resolveValue(useBase) and then rest-destructures a plain object.
curl -sL http://localhost:5803/ | grep -oE 'RESULT[^<]*'Actual:
RESULT {}
Expected:
RESULT {"slug":"ada","avatarId":"abc"}
Because the rest object is empty, profile.avatarId and profile.slug are undefined and the
derived img is { id: undefined, slug: undefined }.
Identical code with the useBase loader and the resolveValue call removed.
curl -sL http://localhost:5803/control/ | grep -oE 'RESULT[^<]*'Actual (correct, HTML-escaped in the raw response):
RESULT {"slug":"ada","avatarId":"abc"}
So the rest destructure alone is fine. Empirically, in this repro the two routes are compiled
differently: the SSR transform of the buggy route imports and calls _restProps, and the control
route keeps the native rest pattern. Inspecting Vite's SSR transform:
=== /src/routes/index.tsx restProps:true
const __vite_ssr_import_0__ = await __vite_ssr_import__(".../core.mjs", {"importedNames":["_restProps"]});
const profile = (0,__vite_ssr_import_0__._restProps)(user, [
=== /src/routes/control/index.tsx restProps:false
const { name, ...profile } = user;
I have not traced why the optimizer picks _restProps in one case and not the other; the
resolveValue chain is simply the smallest shape I found that reliably triggers it. The
misbehavior of _restProps on a plain object is the part that is unambiguous.
_restProps in node_modules/@qwik.dev/core/dist/core.mjs (around line 3865):
const _restProps = (props, omit = [], target = {}) => {
let constPropsTarget = null;
const constProps = props[_CONST_PROPS];
...
const varProps = props[_VAR_PROPS];
for (const key in varProps) { ... }
...
};For a plain object both props[_CONST_PROPS] and props[_VAR_PROPS] are undefined, every loop
body is skipped, and the function returns an empty props proxy.
A plain-object fallback fixes it: when neither symbol is present, copy the object's own enumerable
keys that are not in the omit list into target and return that. (Alternatively, the optimizer
should not rewrite rest destructuring of values that are not component props.)
Any loader that derives its return value through rest destructuring silently loses fields — no exception, no warning, just missing data downstream.