can1357/oh-my-pi · error · OmpTypeError

ParseError: Invalid intersection of default values ${String(

Error message

ParseError: Invalid intersection of default values ${String(ap.def)} & ${String(bp.def)}

What it means

When intersecting two object types that share a property key where BOTH sides declare a default value and those defaults differ (by Object.is), the intersection cannot pick a single default, so the library throws. Intersections must yield one deterministic default for each property.

Source

Thrown at packages/omptype/src/type.ts:2054

		}
		if (members.length === 0) throw new OmpTypeError("intersection has no satisfiable branches");
		return members.length === 1 ? members[0] : { k: "union", members };
	}
	if (a.k === "lit") {
		if (walk(b, a.v) instanceof OmpErrors) throw new OmpTypeError("literal is excluded by the intersection");
		return a;
	}
	if (b.k === "lit") return intersect(b, a);
	if (a.k === "object" && b.k === "object") {
		const props = [...a.props];
		for (const bp of b.props) {
			const index = props.findIndex(prop => prop.key === bp.key);
			if (index < 0) props.push(bp);
			else {
				const ap = props[index];
				const required = (!ap.opt && !ap.hasDefault) || (!bp.opt && !bp.hasDefault);
				if (ap.hasDefault && bp.hasDefault && !Object.is(ap.def, bp.def)) {
					throw new OmpTypeError(
						`ParseError: Invalid intersection of default values ${String(ap.def)} & ${String(bp.def)}`,
					);
				}
				const defaulted = required ? undefined : ap.hasDefault ? ap : bp.hasDefault ? bp : undefined;
				props[index] = {
					key: ap.key,
					opt: ap.opt && bp.opt,
					val: intersect(ap.val, bp.val),
					...(defaulted
						? {
								def: defaulted.def,
								defFactory: defaulted.defFactory,
								hasDefault: true,
								defValidated: defaulted.defValidated,
							}
						: {}),
				};
			}

View on GitHub (pinned to 9690622007)

Solutions

  1. Make the defaults identical on both sides
  2. Remove .default() from one of the two operands so only one side supplies a default
  3. Merge the schemas at the source (one canonical definition) instead of intersecting
  4. Catch OmpTypeError and report which key/defaults conflict

Example fix

// before
intersect(object({retries: number().opt().default(3)}), object({retries: number().opt().default(5)}));
// after
intersect(object({retries: number().opt().default(3)}), object({retries: number().opt()}));
Defensive patterns

Strategy: validation

Validate before calling

function hasConflictingDefaults(a, b, key) {
  const pa = a.props?.find(p => p.key === key);
  const pb = b.props?.find(p => p.key === key);
  return pa?.hasDefault && pb?.hasDefault && !Object.is(pa.def, pb.def);
}

Type guard

const isOmpTypeError = (e: unknown): e is OmpTypeError => e instanceof OmpTypeError;

Try / catch

try {
  const merged = intersect(baseSchema, overrideSchema);
} catch (err) {
  if (err instanceof OmpTypeError && err.message.startsWith('ParseError: Invalid intersection of default values')) {
    logger.warn('conflicting defaults in schema intersection', { message: err.message });
  } else throw err;
}

Prevention

When it happens

Trigger: intersect(object({retries: opt(number).default(3)}), object({retries: opt(number).default(5)})) — same key, two different .default(...) values; typically from merging two schema definitions of the same object.

Common situations: Base schema and override schema both specifying .default() for the same option with different values; a vendor schema and a local refinement both defaulting a field; copy-pasted schema fragments drifting apart.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/c7ef0e3991a85c8e. Report an issue: GitHub.