can1357/oh-my-pi · error · OmpTypeError

type.withJsonSchema cannot wrap schemas with defaults or out

Error message

type.withJsonSchema cannot wrap schemas with defaults or output-changing morphs

What it means

`type.withJsonSchema(schema, json)` attaches JSON Schema metadata to a type, but it cannot faithfully represent types that carry input defaults or output-changing morphs/pipes: those change the value between input and output, and attaching a static JSON Schema would misrepresent them. Such schemas are rejected at wrap time.

Source

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

	/**
	 * Return a validation-only schema that emits `json` verbatim — even when
	 * embedded in an object, array, or union.
	 *
	 * A `.toJsonSchema()` method override cannot survive nesting: a parent schema
	 * emits each child's IR directly and never calls the child's method, so the
	 * override silently disappears from the wire schema. This stores the override
	 * on the IR instead.
	 *
	 * # Errors
	 *
	 * Throws when `schema` has a default or output-changing morph/pipe. A refine
	 * can preserve validation and the input value, but silently discarding a
	 * transformed output would violate the returned {@link Type}.
	 */
	export function withJsonSchema<t, i = t>(schema: Type<t, i>, json: Record<string, unknown>): Type<t, i> {
		const internal = schema as unknown as InternalType;
		if (internal.hasDefault || hasMorph(internal.ir) || internal[kSteps].some(step => step.kind === "pipe")) {
			throw new OmpTypeError("type.withJsonSchema cannot wrap schemas with defaults or output-changing morphs");
		}
		return makeType<t, i>(
			{
				k: "refine",
				base: { k: "unknown" },
				pred: value => {
					const result = schema(value);
					return result instanceof OmpErrors ? result : true;
				},
				expected: schema.expression,
				json: { ...json },
			},
			[],
			{},
		);
	}
}

View on GitHub (pinned to 9690622007)

Solutions

  1. Remove `.default(...)` from the schema, or attach the JSON Schema before adding the default.
  2. Split morphs out: attach the JSON Schema to the pure validation base type, and apply the morph outside the wrapped type.
  3. Replace pipes with plain refinements (predicates that do not transform) if the output equals the input.
  4. Write the JSON Schema by hand for morphing types instead of using withJsonSchema.

Example fix

// before
type.withJsonSchema(type("string").pipe(s => s.trim()), {})
// after
type.withJsonSchema(type("string"), {}).pipe(s => s.trim())
Defensive patterns

Strategy: try-catch

Validate before calling

const canAttachJsonSchema = (schema: unknown) => { const t = schema as InternalType; return !t.hasDefault && !t.expression?.includes('=>'); };

Try / catch

try { return type.withJsonSchema(schema, json); } catch (e) { if (e instanceof OmpTypeError) return stripMorphsThenWrap(schema, json); throw e; }

Prevention

When it happens

Trigger: Calling `type.withJsonSchema(schema, {...})` where the schema was built with `.default(...)`, contains a morph (`.pipe(...)`, `.narrow` returning transformed values), or includes a piped step in its compiled steps.

Common situations: Retrofitting JSON Schema export onto an existing schema collection where some schemas have defaults added by later edits; piping in a transformation before trying to export the schema for docs/OpenAPI generation.

Related errors


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