Skip to content

zOneOf validation errors when exactly one field is provided #3296

@ajinkyap9

Description

@ajinkyap9

Issue description

  • The zOneOf helper incorrectly reports errors when exactly one of two fields is filled. It should only error when both are filled or both are empty.
  • Location: src/utils/zod.ts, function zOneOf(...).
  • Root Cause: The code uses xor(arg[key1] as boolean, arg[key2] as boolean) and treats that as an error case. xor is true when exactly one input is truthy; the function should error only when “not exactly one” is filled (both or neither). Raw boolean coercion is also fragile for “filled” detection (e.g., empty strings vs. undefined).

Expected behavior

  • Validation passes when exactly one of the two keys is “filled” per schema rules.
  • Validation fails when both keys are filled or when neither key is filled.

How to Reproduce

Code-based reproduction

import { z } from 'zod';
import { zOneOf } from '@/utils/zod';

// Treat "filled" as non-empty string for this example
const schema = z
  .object({ a: z.string().optional(), b: z.string().optional() })
  .superRefine(zOneOf<{ a?: string; b?: string }, 'a', 'b'>('a', 'b'));

// Case 1: only a filled → should pass, currently fails
const res1 = schema.safeParse({ a: 'x', b: undefined });

// Case 2: only b filled → should pass, currently fails
const res2 = schema.safeParse({ a: undefined, b: 'y' });

// Case 3: both filled → should fail, may pass
const res3 = schema.safeParse({ a: 'x', b: 'y' });

// Case 4: neither filled → should fail, may pass
const res4 = schema.safeParse({ a: undefined, b: undefined });

// Expected:
// res1.success === true
// res2.success === true
// res3.success === false
// res4.success === false

UI-based reproduction (if a form uses one-of semantics)

  1. Open any form that requires exactly one of two fields (e.g., a mutually exclusive pair).
  2. Fill only the first field and leave the second empty.
  3. Submit the form.
  4. Observe an error raised on both fields, contradicting the intended “exactly one” rule.

Screenshots

No response

Environment

  • apisix version (cmd: apisix version): N/A (services not running locally)
  • OS: Microsoft Windows 11 Home Single Language, 10.0.26200 Build 26200
  • OpenResty / Nginx version (cmd: nginx -V or openresty -V): N/A (services not running locally)
  • etcd version, if have (cmd: run etcd --version): N/A (services not running locally)
  • apisix-dashboard version, if have: master branch, commit bbe05ad (package.json version 0.0.0)
  • Browser version, if have: Google Chrome 144.0.7559.110

Additional context

I can open a PR to fix this and add unit tests.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions