can1357/oh-my-pi · error · OmpTypeError

pipe operands must be functions or Types

Error message

pipe operands must be functions or Types

What it means

`type.pipe(...)` accepts only functions and Types as operands. When an operand is neither a Type (handled above) nor a function, the pipeline builder throws 'pipe operands must be functions or Types' as an OmpTypeError, because there is no way to turn it into a step.

Source

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

			if (
				!forcePipeline &&
				schema[kSteps].length === 0 &&
				!target.hasSteps &&
				!hasMorph(schema.ir) &&
				!hasMorph(target.ir)
			) {
				schema = makeType(intersect(schema.ir, target.ir), [], metaOf(schema));
				continue;
			}
			const out = target.opaqueOutput ? undefined : (target.stepOut ?? target.ir);
			schema = makeType(
				schema.ir,
				[...schema[kSteps], { kind: "pipe", fn: value => target.run(value), out, try: catchErrors }],
				metaOf(schema),
			);
			continue;
		}
		if (typeof candidate !== "function") throw new OmpTypeError("pipe operands must be functions or Types");
		schema = makeType(
			schema.ir,
			[...schema[kSteps], { kind: "pipe", fn: candidate as Step["fn"], try: catchErrors }],
			metaOf(schema),
		);
	}
	return inheritScope(source, schema);
}

function projectIO(ir: IR, io: "in" | "out"): IR {
	switch (ir.k) {
		case "morph":
			return projectIO(io === "in" || ir.out === undefined ? ir.input : ir.out, io);
		case "sub":
			if (io === "out" && ir.schema.opaqueOutput) return { k: "unknown" };
			return projectIO(io === "out" ? (ir.schema.stepOut ?? ir.schema.ir) : ir.schema.ir, io);
		case "array":
			return { ...ir, el: projectIO(ir.el, io) };

View on GitHub (pinned to 9690622007)

Solutions

  1. Pass an actual function: `type('string').pipe(s => s.trim())`.
  2. Pass a Type created by omptype: `.pipe(otherType)`.
  3. Check for undefined operands from import cycles or missing arguments; log the operand before piping.
  4. If piping into another library's validator, wrap it: `.pipe(v => otherValidator.parse(v))`.

Example fix

// before
type('string').pipe(someValidator); // plain object, throws

// after
type('string').pipe(v => someValidator.parse(v));
Defensive patterns

Strategy: validation

Validate before calling

const operands = [step1, step2];
for (const op of operands) {
  if (typeof op !== 'function' && !(op instanceof OmpType)) {
    throw new Error(`bad pipe operand: ${String(op)}`);
  }
}
type('string').pipe(...operands);

Type guard

function isPipeOperand(op) {
  return typeof op === 'function' || op instanceof OmpType;
}

Try / catch

try {
  const T = base.pipe(...steps);
} catch (e) {
  if (e instanceof OmpTypeError && e.message.includes('pipe operands')) {
    // filter steps: steps.filter(isPipeOperand) and retry
  } else throw e;
}

Prevention

When it happens

Trigger: `type('string').pipe(42)`, `.pipe(undefined)` (e.g. an optional callback not provided), passing a plain object or string where a transform was intended, or a mis-imported identifier that is undefined at runtime.

Common situations: Circular-import shadowing making a transform function undefined; passing a schema-like object from another library expecting auto-conversion; typos in the transform function reference.

Related errors


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