Description
Versions
| Package |
Version |
orval |
8.10.0 |
@orval/query |
(bundled with orval@8.10.0) |
@tanstack/solid-query |
5.100.8 |
typescript |
5.9.3 |
Summary
With output.client: 'solid-query' against @tanstack/solid-query@^5.71.5 (the
use*-prefix regime), every OpenAPI operation that becomes a mutation produces
generated code that fails type-check on two independent points:
-
SolidMutationOptions is not exported by @tanstack/solid-query v5.
Orval emits import type { …, SolidMutationOptions, … } from '@tanstack/solid-query',
which fails with TS2724: '"@tanstack/solid-query"' has no exported member named 'SolidMutationOptions'. Did you mean 'MutationOptions'? on every
generated file containing a mutation.
-
mutation?: UseMutationOptions<…> is typed as a function but used as an
object. In @tanstack/solid-query v5,
UseMutationOptions<TData, TError, TVariables, TOnMutateResult> is declared as
Accessor<MutationOptions<TData, TError, TVariables, TOnMutateResult>> — i.e.
a getter function, not the options object. The emitted runtime spreads
it as a plain object ({...options.mutation}) and the intended call site is
usePutXxx({ mutation: { onSuccess, … } }). TypeScript rejects that with
TS2353: Object literal may only specify known properties, and 'onSuccess' does not exist in type 'UseMutationOptions<…>'.
Both stem from getSolidQueryImports / createSolidAdapter in
@orval/query referring to type aliases that solid-query v5 no longer exports
under the same name (or with the same shape).
Reproduction
A minimal repro: any spec with one non-GET endpoint + an orval.config.ts like:
import { defineConfig } from 'orval';
export default defineConfig({
example: {
input: { target: './openapi.json' },
output: {
target: './src/api/generated',
mode: 'tags-split',
client: 'solid-query',
httpClient: 'axios',
override: {
mutator: { path: './src/lib/client.ts', name: 'client' },
// Leave query.useQuery / query.useMutation unset — Orval's per-verb
// defaults are correct.
query: { signal: true },
},
},
},
});
@tanstack/solid-query installed at any version ≥ 5.71.5 (so the emitter
takes the use-prefix branch).
Run orval and then tsc --noEmit:
src/api/generated/example/example.ts:17:3 - error TS2724:
'"@tanstack/solid-query"' has no exported member named 'SolidMutationOptions'.
Did you mean 'MutationOptions'?
src/api/generated/example/example.ts:76:7 - error TS2353:
Object literal may only specify known properties, and 'onSuccess' does
not exist in type 'UseMutationOptions<OrderChangedEvent, ProblemDetails,
{data: SomeBody}, unknown>'.
What the emitter produces (excerpt)
// AUTO-GENERATED by orval@8.10.0
import {
useMutation,
useQuery
} from '@tanstack/solid-query';
import type {
// …
SolidMutationOptions, // ← does not exist in solid-query@5
UseMutationOptions, // ← solid-query@5: Accessor<MutationOptions<...>>
UseMutationResult,
// …
} from '@tanstack/solid-query';
export const getPutXxxMutationOptions = <TError = ProblemDetails, TContext = unknown>(
options?: { mutation?: UseMutationOptions<…, …, { data: SomeBody }, TContext> },
): SolidMutationOptions<…, …, { data: SomeBody }, TContext> => {
const { mutation: mutationOptions } = options ?
/* … */
: { mutation: { mutationKey, } };
// mutationOptions is typed as Accessor<MutationOptions> (a function) but
// spread here as an object:
return { mutationFn, ...mutationOptions }
}
export const usePutXxx = <TError = ProblemDetails, TContext = unknown>(
options?: { mutation?: UseMutationOptions<…> },
queryClient?: () => QueryClient,
): UseMutationResult<…> => {
return useMutation(() => getPutXxxMutationOptions(options), queryClient);
}
Pinpointing the bug
@orval/query/dist/index.mjs (v8.10.0):
- Line 707–741 —
getSolidQueryImports(prefix): hardcodes
{ name: "SolidMutationOptions" } in the import list regardless of prefix.
- Line 805–811 —
getSolidQueryDependencies: passes "use" when
isSolidQueryWithUsePrefix(packageJson) (solid-query ≥ 5.71.5) is true, but
the dependencies list still references SolidMutationOptions.
- Line 861–866 —
isSolidQueryWithUsePrefix: detects the prefix regime
via compareVersions(version, "5.71.5").
- Line 1113–1128 —
createSolidAdapter / getOptionsReturnTypeName:
unconditionally returns "SolidMutationOptions" for type === "mutation".
The use-prefix migration that landed in solid-query 5.71.5 dropped the
Solid*Options aliases. The emitter wasn't updated to match.
Expected behaviour
For @tanstack/solid-query@^5.71.5 the emitter should produce code that:
- Imports types that solid-query v5 actually re-exports. For the mutation
options return type that means MutationOptions (re-exported from
@tanstack/query-core).
- Types the user-facing
mutation?: field as the plain
MutationOptions<…> object, matching the way the emitted runtime spreads
it (and the way every example in the docs invokes the hook with { mutation: { onSuccess, … } }).
Equivalent generated code:
import type {
// …
MutationOptions, // ← real solid-query@5 export
UseMutationOptions, // ← keep if still used for Accessor positions
UseMutationResult,
} from '@tanstack/solid-query';
export const getPutXxxMutationOptions = <TError, TContext>(
options?: { mutation?: MutationOptions<…> },
): MutationOptions<…> => { /* … */ };
export const usePutXxx = <TError, TContext>(
options?: { mutation?: MutationOptions<…> },
queryClient?: () => QueryClient,
): UseMutationResult<…> => {
return useMutation(() => getPutXxxMutationOptions(options), queryClient);
};
Suggested fix
Mechanical change in @orval/query:
getSolidQueryImports — when the use-prefix branch is taken, replace
{ name: "SolidMutationOptions" } with { name: "MutationOptions" } (and
the analogous swap for SolidQueryOptions / SolidInfiniteQueryOptions if
they're affected the same way).
createSolidAdapter.getOptionsReturnTypeName — when use-prefix is
active, return "MutationOptions" instead of "SolidMutationOptions".
- Mutation options field type — switch the user-facing
mutation?: UseMutationOptions<…> to mutation?: MutationOptions<…> so the
call-site object literal validates. (This also matches the React Query
emitter's behaviour where the field accepts an object, not an Accessor.)
Adding a small test fixture under samples/ that uses a non-GET operation
with client: 'solid-query' and @tanstack/solid-query@^5.71.5 and asserts
the output passes tsc would prevent the regression.
Current workaround (temporary)
Until this is fixed upstream, we apply a small post-processor as part of
api:gen that rewrites the affected files (idempotent, runs after orval):
- drop
SolidMutationOptions from imports + replace usages with
MutationOptions
- rewrite
mutation?: UseMutationOptions<…> → mutation?: MutationOptions<…>
- ensure
MutationOptions is added to the type-only import block
Happy to share the script and / or open a PR with the emitter fix if it's
useful.
Additional context
Spec uses [Consumes]-content-type dispatch on the .NET side, so
override.splitByContentType: true is set. Doesn't seem to interact with this
bug — the same break shows up on plain POST endpoints without content-type
splitting.
Output client
axios
Configuration (orval.config)
// orval.config.ts
import { defineConfig } from 'orval';
export default defineConfig({
example: {
input: {
target: './openapi.json',
},
output: {
target: './src/api/generated',
mode: 'tags-split',
client: 'solid-query',
httpClient: 'axios',
override: {
splitByContentType: true,
mutator: {
path: './src/lib/api/client.ts',
name: 'client',
},
// Leave query.useQuery / query.useMutation unset — Orval's per-verb
// defaults (GET → useQuery, non-GET → useMutation) are correct.
// Setting useQuery: true globally suppresses mutation emission via the
// `if (verb !== GET && isQuery) isMutation = false` branch in
// @orval/query/dist/index.mjs:2139.
query: {
signal: true,
},
},
},
},
});
Environment
System:
OS: Windows 11 Pro 10.0.26200
Shell: PowerShell 7 (pwsh)
Binaries:
Node: 22.x (via Bun’s built-in)
bun: 1.2.x
Package manager: bun (workspace)
npmPackages:
orval: ^8.10.0 => 8.10.0
@tanstack/solid-query: ^5.100.8 => 5.100.8
@tanstack/solid-query-devtools: ^5.100.8 => 5.100.8
solid-js: ^1.9.12 => 1.9.12
typescript: ^5.9.3 => 5.9.3
vite: ^6.4.2 => 6.4.2
vite-plugin-solid: ^2.11.12 => 2.11.12
axios: ^1.16.0 => 1.16.0
Filed from a Bun workspace; same break reproduces under pnpm / npm (the
emitter only consults package.json for the solid-query version, not the
package manager).
Expected behavior
Generated code for a mutation operation should compile under tsc --noEmit
against @tanstack/solid-query@^5.71.5. Specifically:
- The type-only import block must reference names that solid-query v5 actually
re-exports — MutationOptions (plain TanStack-core object) at the return
position of getXxxMutationOptions, not the non-existent
SolidMutationOptions.
- The user-facing
options.mutation parameter must be typed as the plain
MutationOptions<…> object (matching the way the emitted runtime spreads
it with {...options.mutation} and the way callers invoke the hook with
usePutXxx({ mutation: { onSuccess, … } })), not UseMutationOptions<…> —
which in solid-query v5 is an Accessor<MutationOptions<…>> (a function).
Concretely, the emitter should produce:
import type {
MutationOptions,
UseMutationResult,
// (UseMutationOptions only where an Accessor is genuinely required)
} from '@tanstack/solid-query';
export const getPutXxxMutationOptions = <TError, TContext>(
options?: { mutation?: MutationOptions<…, TError, { data: SomeBody }, TContext> },
): MutationOptions<…, TError, { data: SomeBody }, TContext> => { /* … */ };
export const usePutXxx = <TError, TContext>(
options?: { mutation?: MutationOptions<…> },
queryClient?: () => QueryClient,
): UseMutationResult<…> => {
return useMutation(() => getPutXxxMutationOptions(options), queryClient);
};
Actual behavior
Running orval against the config + spec below, then tsc --noEmit:
src/api/generated/example/example.ts:17:3 - error TS2724:
'"@tanstack/solid-query"' has no exported member named 'SolidMutationOptions'.
Did you mean 'MutationOptions'?
17 SolidMutationOptions,
~~~~~~~~~~~~~~~~~~~~
src/api/generated/example/example.ts:76:7 - error TS2353:
Object literal may only specify known properties, and 'onSuccess' does not
exist in type 'UseMutationOptions<OrderChangedEvent, ProblemDetails,
{ data: SomeBody; }, unknown>'.
76 onSuccess: (data, vars) => { /* … */ },
~~~~~~~~~
Every generated *.ts file containing at least one non-GET endpoint fails to
compile — for our internal POS-Web codebase that's 5 of 8 tag-split files
(branch, kitchen, me-kds-state, order, services).
If users try to recover by // @ts-expect-error-ing the import, the second
error still fires at every call site, and the {...options.mutation} spread
at runtime silently drops onSuccess / onMutate / onSettled because
spreading a function evaluates to {} — so lifecycle callbacks never fire.
OpenAPI document (minimal, if applicable)
openapi: 3.0.3
info:
title: Demo
version: 0.0.0
paths:
/pets:
post:
operationId: createPet
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
responses:
'201':
description: Created
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
components:
schemas:
Pet:
type: object
required: [id, name]
properties:
id:
type: string
format: uuid
name:
type: string
A single POST is sufficient to reproduce both TS2724 and TS2353. Adding a GET
alongside confirms that only the mutation-bearing operations fail — the
GET output remains clean.
Additional context
-
Workaround in use. A ~100-line post-processor running after orval in
the api:gen script. Idempotent regex rewrite on every file under
src/api/generated/:
- drop
SolidMutationOptions from the import type {…} block;
- swap
SolidMutationOptions → MutationOptions in type positions;
- swap
mutation?: UseMutationOptions<…> → mutation?: MutationOptions<…>;
- add
MutationOptions to the type-only import block when needed.
Happy to share the script if it'd help craft the upstream fix or a
regression sample — it pins exactly where the emitter diverges from what
solid-query v5 accepts.
-
Solid Query 5.71.5 changelog reference. The use*-prefix migration that
triggers isSolidQueryWithUsePrefix (@orval/query/dist/index.mjs:861-866)
appears to be the release that dropped the Solid*Options aliases from the
package's public surface. Bisecting @tanstack/solid-query versions around
5.71.x against this emitter would identify the exact pre-rename version
that still works.
-
Related but distinct concern (separate issue worth opening). Setting
output.override.query.useQuery: true globally — a config that looks
natural for "I want useQuery hooks generated" — silently suppresses every
useMutation hook. Root cause is the conflict resolver at
@orval/query/dist/index.mjs:2131-2140:
const effectiveUseQuery = … ?? override.query.useQuery ?? verb === Verbs.GET;
const effectiveUseMutation = … ?? override.query.useMutation ?? verb !== Verbs.GET;
let isQuery = effectiveUseQuery || …;
let isMutation = effectiveUseMutation && verb !== Verbs.GET;
if (verb !== Verbs.GET && isQuery) isMutation = false; // ← suppresses
if (verb === Verbs.GET && isMutation) isQuery = false;
With useQuery: true set globally, isQuery becomes true for every verb,
the conflict resolver forces isMutation = false, and the matching
useMutation: true flag never gets a chance to fire. A docs warning (or a
config-validation log) would have caught this for us much earlier — happy
to file a separate issue if you'd prefer it tracked independently rather
than as a footnote here.
-
Willing to PR. I can draft the emitter fix + a samples/solid-query-v5/
fixture (one GET, one POST, asserts tsc --noEmit passes) if a maintainer
confirms the direction in [Suggested fix].
Description
Versions
orval8.10.0@orval/queryorval@8.10.0)@tanstack/solid-query5.100.8typescript5.9.3Summary
With
output.client: 'solid-query'against@tanstack/solid-query@^5.71.5(theuse*-prefix regime), every OpenAPI operation that becomes a mutation producesgenerated code that fails type-check on two independent points:
SolidMutationOptionsis not exported by@tanstack/solid-queryv5.Orval emits
import type { …, SolidMutationOptions, … } from '@tanstack/solid-query',which fails with
TS2724: '"@tanstack/solid-query"' has no exported member named 'SolidMutationOptions'. Did you mean 'MutationOptions'?on everygenerated file containing a mutation.
mutation?: UseMutationOptions<…>is typed as a function but used as anobject. In
@tanstack/solid-queryv5,UseMutationOptions<TData, TError, TVariables, TOnMutateResult>is declared asAccessor<MutationOptions<TData, TError, TVariables, TOnMutateResult>>— i.e.a getter function, not the options object. The emitted runtime spreads
it as a plain object (
{...options.mutation}) and the intended call site isusePutXxx({ mutation: { onSuccess, … } }). TypeScript rejects that withTS2353: Object literal may only specify known properties, and 'onSuccess' does not exist in type 'UseMutationOptions<…>'.Both stem from
getSolidQueryImports/createSolidAdapterin@orval/queryreferring to type aliases that solid-query v5 no longer exportsunder the same name (or with the same shape).
Reproduction
A minimal repro: any spec with one non-GET endpoint + an
orval.config.tslike:@tanstack/solid-queryinstalled at any version ≥5.71.5(so the emittertakes the use-prefix branch).
Run
orvaland thentsc --noEmit:What the emitter produces (excerpt)
Pinpointing the bug
@orval/query/dist/index.mjs(v8.10.0):getSolidQueryImports(prefix): hardcodes{ name: "SolidMutationOptions" }in the import list regardless of prefix.getSolidQueryDependencies: passes"use"whenisSolidQueryWithUsePrefix(packageJson)(solid-query ≥ 5.71.5) is true, butthe dependencies list still references
SolidMutationOptions.isSolidQueryWithUsePrefix: detects the prefix regimevia
compareVersions(version, "5.71.5").createSolidAdapter/getOptionsReturnTypeName:unconditionally returns
"SolidMutationOptions"fortype === "mutation".The use-prefix migration that landed in solid-query 5.71.5 dropped the
Solid*Optionsaliases. The emitter wasn't updated to match.Expected behaviour
For
@tanstack/solid-query@^5.71.5the emitter should produce code that:options return type that means
MutationOptions(re-exported from@tanstack/query-core).mutation?:field as the plainMutationOptions<…>object, matching the way the emitted runtime spreadsit (and the way every example in the docs invokes the hook with
{ mutation: { onSuccess, … } }).Equivalent generated code:
Suggested fix
Mechanical change in
@orval/query:getSolidQueryImports— when the use-prefix branch is taken, replace{ name: "SolidMutationOptions" }with{ name: "MutationOptions" }(andthe analogous swap for
SolidQueryOptions/SolidInfiniteQueryOptionsifthey're affected the same way).
createSolidAdapter.getOptionsReturnTypeName— when use-prefix isactive, return
"MutationOptions"instead of"SolidMutationOptions".mutation?: UseMutationOptions<…>tomutation?: MutationOptions<…>so thecall-site object literal validates. (This also matches the React Query
emitter's behaviour where the field accepts an object, not an Accessor.)
Adding a small test fixture under
samples/that uses a non-GET operationwith
client: 'solid-query'and@tanstack/solid-query@^5.71.5and assertsthe output passes
tscwould prevent the regression.Current workaround (temporary)
Until this is fixed upstream, we apply a small post-processor as part of
api:genthat rewrites the affected files (idempotent, runs afterorval):SolidMutationOptionsfrom imports + replace usages withMutationOptionsmutation?: UseMutationOptions<…>→mutation?: MutationOptions<…>MutationOptionsis added to the type-only import blockHappy to share the script and / or open a PR with the emitter fix if it's
useful.
Additional context
Spec uses
[Consumes]-content-type dispatch on the .NET side, sooverride.splitByContentType: trueis set. Doesn't seem to interact with thisbug — the same break shows up on plain POST endpoints without content-type
splitting.
Output client
axios
Configuration (orval.config)
Environment
Filed from a Bun workspace; same break reproduces under
pnpm/npm(theemitter only consults
package.jsonfor the solid-query version, not thepackage manager).
Expected behavior
Generated code for a mutation operation should compile under
tsc --noEmitagainst
@tanstack/solid-query@^5.71.5. Specifically:re-exports —
MutationOptions(plain TanStack-core object) at the returnposition of
getXxxMutationOptions, not the non-existentSolidMutationOptions.options.mutationparameter must be typed as the plainMutationOptions<…>object (matching the way the emitted runtime spreadsit with
{...options.mutation}and the way callers invoke the hook withusePutXxx({ mutation: { onSuccess, … } })), notUseMutationOptions<…>—which in solid-query v5 is an
Accessor<MutationOptions<…>>(a function).Concretely, the emitter should produce:
Actual behavior
Running
orvalagainst the config + spec below, thentsc --noEmit:Every generated
*.tsfile containing at least one non-GET endpoint fails tocompile — for our internal POS-Web codebase that's 5 of 8 tag-split files
(branch, kitchen, me-kds-state, order, services).
If users try to recover by
// @ts-expect-error-ing the import, the seconderror still fires at every call site, and the
{...options.mutation}spreadat runtime silently drops
onSuccess/onMutate/onSettledbecausespreading a function evaluates to
{}— so lifecycle callbacks never fire.OpenAPI document (minimal, if applicable)
A single POST is sufficient to reproduce both TS2724 and TS2353. Adding a GET
alongside confirms that only the mutation-bearing operations fail — the
GET output remains clean.
Additional context
Workaround in use. A ~100-line post-processor running after
orvalinthe
api:genscript. Idempotent regex rewrite on every file undersrc/api/generated/:SolidMutationOptionsfrom theimport type {…}block;SolidMutationOptions→MutationOptionsin type positions;mutation?: UseMutationOptions<…>→mutation?: MutationOptions<…>;MutationOptionsto the type-only import block when needed.Happy to share the script if it'd help craft the upstream fix or a
regression sample — it pins exactly where the emitter diverges from what
solid-query v5 accepts.
Solid Query 5.71.5 changelog reference. The
use*-prefix migration thattriggers
isSolidQueryWithUsePrefix(@orval/query/dist/index.mjs:861-866)appears to be the release that dropped the
Solid*Optionsaliases from thepackage's public surface. Bisecting
@tanstack/solid-queryversions around5.71.x against this emitter would identify the exact pre-rename version
that still works.
Related but distinct concern (separate issue worth opening). Setting
output.override.query.useQuery: trueglobally — a config that looksnatural for "I want useQuery hooks generated" — silently suppresses every
useMutationhook. Root cause is the conflict resolver at@orval/query/dist/index.mjs:2131-2140:With
useQuery: trueset globally,isQuerybecomes true for every verb,the conflict resolver forces
isMutation = false, and the matchinguseMutation: trueflag never gets a chance to fire. A docs warning (or aconfig-validation log) would have caught this for us much earlier — happy
to file a separate issue if you'd prefer it tracked independently rather
than as a footnote here.
Willing to PR. I can draft the emitter fix + a
samples/solid-query-v5/fixture (one GET, one POST, asserts
tsc --noEmitpasses) if a maintainerconfirms the direction in [Suggested fix].