can1357/oh-my-pi · error · OmpTypeError
thunk must return a Type (was ${typeof resolved})
Error message
thunk must return a Type (was ${typeof resolved}) What it means
Function definitions in omptype are thunks: zero-argument functions lazily producing a type/embeddable schema. parseDef invokes the thunk with Reflect.apply(def, undefined, []) and requires the return value to satisfy isEmbedded; anything else (string, number, plain object, undefined, class instance) triggers this error naming the typeof the bad return value.
Source
Thrown at packages/omptype/src/ir.ts:1516
}
if (parsed.optional) {
throw new OmpTypeError(`optional "?" marker is only valid on object property values`);
}
return parsed.ir;
}
if (Array.isArray(def)) {
if (def.length === 3 && def[1] === "=") {
throw new OmpTypeError("A default may only be specified for an object property or tuple element");
}
return parseArrayExpression(def, resolve);
}
if (def instanceof RegExp) return patternIR(def);
if (def instanceof Date) return { k: "lit", v: def };
if (isEmbedded(def)) return embed(def);
if (typeof def === "function") {
const resolved = Reflect.apply(def, undefined, []);
if (!isEmbedded(resolved)) {
throw new OmpTypeError(`thunk must return a Type (was ${typeof resolved})`);
}
return embed(resolved);
}
if (isObjectDefinition(def)) return parseObjectDefinition(def, resolve);
throw new OmpTypeError(`unsupported definition ${String(def)} (was ${typeof def})`);
}
/** Whether `ir` needs no construction-time normalization or morph analysis. */
export function isSimpleIR(ir: IR): boolean {
const cached = ir[kSimpleOwner] === ir ? ir[kSimple] : undefined;
if (cached !== undefined) return cached;
const simple = scanSimpleIR(ir);
ir[kSimple] = simple;
ir[kSimpleOwner] = ir;
return simple;
}
function scanSimpleIR(ir: IR): boolean {View on GitHub (pinned to 9690622007)
Solutions
- Return a Type from the thunk: () => type("string") rather than () => "string"
- Ensure lazy/async-loaded values are fully constructed types before returning
- Check memoization wrappers preserve the Type instance
- Inspect the reported typeof in the message to identify what was actually returned
Example fix
// before
const T = type(() => "string|number")
// after
const T = type(() => type("string|number")) Defensive patterns
Strategy: type-guard
Validate before calling
function assertThunkReturnsType(fn) {
if (typeof fn !== "function") return;
const r = fn();
if (r == null || typeof r !== "object")
throw new Error("thunk must return a Type, not " + typeof r);
} Type guard
const returnsType = (fn) => { try { const r = fn(); return r != null && typeof r === "object"; } catch { return false; } }; Try / catch
try { const T = type(thunk); } catch (e) {
if (String(e.message).startsWith("thunk must return a Type")) {
// wrap the thunk body's return in type(...) and retry
} else throw e;
} Prevention
- Always return type(...) results from thunks, never raw def strings or plain values
- Check lazy imports resolve to constructed types, not def data
- Add a unit test exercising each recursive/lazy thunk
When it happens
Trigger: Passing a function to type()/parseDef whose body returns something other than a Type/embeddable schema — e.g. returning a raw string definition instead of calling type() on it, returning undefined from a forgetful thunk, or a memoized getter returning a cached non-type value.
Common situations: Recursive type helpers where the thunk returns def data instead of a parsed type; lazy imports resolving to the wrong export; typos like () => "string" instead of () => type("string").
Related errors
- invalid index signature pattern ${keyDefinition}
- duplicate object key ${String(key)}
- optional key ${String(key)} cannot specify a default
- A default may only be specified for an object property or tu
- optional "?" marker is only valid on object property values
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/2f47526931485d10.
Report an issue: GitHub.