form icon indicating copy to clipboard operation
form copied to clipboard

Validation returns raw form value instead of parsed value (standard schema validators)

Open kazukiiwas opened this issue 3 months ago • 5 comments

Describe the bug

When validating with a “standard” schema, the form returns the original (unparsed) value instead of the schema-parsed value. This causes:

  • Untrusted raw values from the frontend to bypass validation/sanitization, which is unsafe.
  • Common transformation use cases (e.g., trimming, type coercion, normalization) to be skipped.

Your minimal, reproducible example

https://stackblitz.com/edit/stackblitz-starters-doaombkp?file=app%2Fpage.tsx

Steps to reproduce

  • Click the Submit button.
  • Inspect the result: you can confirm the unparsed value under the actual key.

Expected behavior

On successful validation (no issues), the validator should return the parsed value.

How often does this bug happen?

Every time

Screenshots or Videos

Image Image

Platform

  • MacOS
  • Chrome

TanStack Form adapter

react-form

TanStack Form version

1.19.3

TypeScript version

5.2.2

Additional context

Proposed change:

packages/form-core/src/standardSchemaValidator.ts: L82

- if (!result.issues) return;
+ if (!result.issues) return { ok: true, data: result.value };

packages/react-form/src/nextjs(remix,start)/createServerValidate.ts

- const onServerError = await runValidator({
-   value: values,
-   validationSource: "form"
- });
- if (!onServerError) return values;
+ const validated = await runValidator({
+   value: values,
+   validationSource: "form"
+ });
+ if (validated.ok) return validated.data;

kazukiiwas avatar Sep 03 '25 10:09 kazukiiwas

I believe this is expected behaviour: https://tanstack.com/form/v1/docs/framework/react/guides/submission-handling#transforming-data-with-standard-schemas

While Tanstack Form provides Standard Schema support for validation, it does not preserve the Schema's output data.

kaboofdotdev avatar Sep 04 '25 03:09 kaboofdotdev

My concern is on the server side — specifically, that the createServerValidate function isn’t working.

kazukiiwas avatar Sep 04 '25 16:09 kazukiiwas

I'm using transforms with valibot like:

const CorsOriginsSchema = v.pipe(
  v.string(),
  v.transform((value) => {
    if (value.trim() === "") {
      return [];
    }
    return value.split(",").map((origin) => origin.trim());
  })
);

and I need to call an api with the transformed output type of the schema so I seem to need to run it twice:

const form = useAppForm({
    defaultValues,
    validators: {
      onSubmit: SomeFormSchema
    },
    onSubmit: async (theForm) => {
      // Validator already checked validity, but didn't apply transforms.
      // Parse again to get the transformed output type.
      // Since validator passed, this parse is guaranteed to succeed. 
      // FIXME: there's gotta be a better way?
      const transformedData = v.parse(SomeFormSchema, theForm.value);
      await doStuff({ transformedData });
    }
  });

This is per the suggestion at https://tanstack.com/form/v1/docs/framework/react/guides/submission-handling#transforming-data-with-standard-schemas, but I don't like it…

I understand this might be a hard problem, but maybe there's a simple way to just remove the "validators" field here and do manual validation in the onSubmit, but programmatically kinda one-liner?

I mean in this case with valibot that would be with safeParse, something like:

const result = v.safeParse(SomeFormSchema, theForm.value);
if(result.success)
      await doStuff({ result.output });
else {
   pushTheErrors(result.issues)
}

I guess the issue is to transform the result.issues valibot specific structure to the tanstack form way and I guess there is code somewhere to do that, but I can't easily find it.

Anyway, I'd be fine with that solution, but running twice feels smelly to me.

hedefalk avatar Nov 05 '25 12:11 hedefalk

I just stumbled upon this as well. I'm glad it was mentioned in the docs, Took quite a while to figure out where the bug in our system was coming from. But I would really expect/prefer the transformed values to be returned, not the og input. Maybe as a separate property if we want to keep things backwards compatible?

onSubmit: ({value, transformedValue}) => doStuff(transformedValue)

kabo avatar Nov 18 '25 21:11 kabo

@kabo the issue with combining all the types from different validators still persists

Jazzmanpw avatar Nov 28 '25 21:11 Jazzmanpw