colinhacks/zod · error · Error

Unmergable intersection. Error path

Error message

Unmergable intersection. Error path: ${JSON.stringify(merged.mergeErrorPath)}

What it means

Thrown by handleIntersectionResults after calling mergeValues(left.value, right.value) when the two parsed intersection halves cannot be structurally merged (mergeValues returns { valid: false, mergeErrorPath }). mergeValues only knows how to combine plain objects (recursively) and same-length arrays; any other combination (string ∩ number, object ∩ primitive, differing scalar types) is unmergable, and the recorded path (JSON.stringified) pinpoints the deepest conflict.

Solutions

  1. Inspect mergeErrorPath to find the exact conflicting field and align the types on both sides.
  2. If only one side should win, replace z.intersection() with z.pipe() or a manual .transform().
  3. Ensure both halves agree on the type of every shared key (object intersections) or that scalars match.
  4. For arrays, ensure both sides expect the same length and element structure.

Example fix

// before
const S = z.intersection(z.string(), z.number());
// after — pick one scalar
const S = z.string();
// or merge compatible object shapes
const S = z.intersection(
  z.object({ id: z.string() }),
  z.object({ id: z.string(), name: z.string() })
);
Defensive patterns

Strategy: try-catch

Try / catch

try {
  return z.intersection(A, B).parse(input);
} catch (e) {
  if (/Unmergable intersection/.test((e as Error).message)) {
    // Align types in A and B, or fall back to a single schema / pipe
    return A.parse(input);
  }
  throw e;
}

Prevention

When it happens

Trigger: z.intersection(z.string(), z.number()); z.intersection(z.object({a:z.string()}), z.array(z.string())); arrays of differing lengths; nested field conflicts like z.object({a: z.string()}) intersected with z.object({a: z.number()}) — the parse succeeds on both sides but merging the values fails.

Common situations: Combining two branded/refined scalar schemas whose underlying types differ; intersecting an object with a record; upgrading from a version that silently took one side to one that enforces structural compatibility; user composes schemas from multiple modules that were not designed to overlap.

Related errors


AI-assisted analysis of colinhacks/zod@2d90846af9 (2026-08-11). Data as JSON: /api/errors/0362a067cd955a44. Report an issue: GitHub.

Appendix: source

Thrown at packages/zod/src/v4/core/schemas.ts:2609

        unrecKeys.get(k)!.r = true;
      }
    } else {
      result.issues.push(iss);
    }
  }

  // Report only keys unrecognized by BOTH sides
  const bothKeys = [...unrecKeys].filter(([, f]) => f.l && f.r).map(([k]) => k);
  if (bothKeys.length && unrecIssue) {
    result.issues.push({ ...unrecIssue, keys: bothKeys });
  }

  if (util.aborted(result)) return result;

  const merged = mergeValues(left.value, right.value);

  if (!merged.valid) {
    throw new Error(`Unmergable intersection. Error path: ` + `${JSON.stringify(merged.mergeErrorPath)}`);
  }

  result.value = merged.data;
  return result;
}

/////////////////////////////////////////
/////////////////////////////////////////
//////////                     //////////
//////////      $ZodTuple      //////////
//////////                     //////////
/////////////////////////////////////////
/////////////////////////////////////////

export interface $ZodTupleDef<
  T extends util.TupleItems = readonly $ZodType[],
  Rest extends SomeType | null = $ZodType | null,
> extends $ZodTypeDef {

View on GitHub (pinned to 2d90846af9)