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
- Inspect mergeErrorPath to find the exact conflicting field and align the types on both sides.
- If only one side should win, replace z.intersection() with z.pipe() or a manual .transform().
- Ensure both halves agree on the type of every shared key (object intersections) or that scalars match.
- 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
- Only intersect schemas whose parsed outputs are structurally mergeable (objects with compatible keys, or matching scalars).
- Inspect mergeErrorPath to localize the conflict.
- Prefer z.pipe() when you need one side to win rather than a true merge.
- Unit-test intersection parsing with representative inputs.
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
- Async schemas not supported in object keys currently
- invalid_arguments
- invalid_return_type
- .merge() cannot be used on object schemas containing…
- Not a ZodError
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)