can1357/oh-my-pi · error · OmpTypeError
record keys must use a string schema
Error message
record keys must use a string schema
What it means
`omptype.record(keySchema, valueSchema)` requires the key schema to be a plain string schema (checked via `isStringKeyIR`); anything else (number keys, enums, unions, transforms) throws this OmpTypeError. Thrown from `record()` at packages/omptype/src/zod.ts:310.
Source
Thrown at packages/omptype/src/zod.ts:310
return decorate(schemaFromIR<Values[number]>(type.enumerated(...values).ir));
};
export { enumSchema as enum };
export const union = <
const Schemas extends readonly [ZodLikeSchema<unknown>, ZodLikeSchema<unknown>, ...ZodLikeSchema<unknown>[]],
>(
schemas: Schemas,
): ZodLikeSchema<UnionOutput<Schemas>> =>
decorate(schemaFromIR({ k: "union", members: schemas.map(schema => embed(schema)) }));
export const array = <Element>(element: ZodLikeSchema<Element>): ZodLikeSchema<Element[]> =>
decorate(schemaFromIR({ k: "array", el: embed(element) }));
export const object = <const S extends Shape>(shape: S): ZodLikeSchema<Simplify<ObjectOutput<S>>> =>
objectSchema(shape);
export const record = <Key extends string, Value>(
keySchema: ZodLikeSchema<Key>,
valueSchema: ZodLikeSchema<Value>,
): ZodLikeSchema<Record<string, Value>> => {
if (!isStringKeyIR(keySchema.ir)) throw new OmpTypeError("record keys must use a string schema");
const base = schemaFromIR<Record<string, Value>>({
k: "object",
props: [],
index: embed(valueSchema),
extras: "keep",
});
const checked = base.narrow((value, ctx: NarrowContext) => {
for (const key in value) {
if (keySchema(key) instanceof type.errors) return ctx.mustBe("a record with valid string keys");
}
return true;
});
return decorate(checked);
};
export const unknown = (): ZodLikeSchema<unknown> => decorate(schemaFromIR(type.unknown.ir));
export const any = (): ZodLikeSchema<unknown> => decorate(schemaFromIR(type.unknown.ir));
const nullSchema = (): ZodLikeSchema<null> => decorate(type.raw("null") as unknown as Decoratable<null>);
const undefinedSchema = (): ZodLikeSchema<undefined> =>View on GitHub (pinned to 9690622007)
Solutions
- Use t.string() as the key schema
- If keys must be restricted to a known set, use t.object({...}) with those props instead of record()
- Wrap enum/number keys in a transformation: keep record keys as t.string() and validate values separately
Example fix
// before
omptype.record(omptype.enum(["a", "b"]), t.number())
// after
omptype.record(t.string(), t.number())
// or for fixed keys:
omptype.object({ a: t.number(), b: t.number() }) Defensive patterns
Strategy: type-guard
Validate before calling
function hasPlainStringKeys(schema: ZodLikeSchema<unknown>): boolean {
return schema.ir.k === "string";
}
if (!hasPlainStringKeys(keySchema)) throw new Error("record key schema must be t.string()"); Type guard
function isPlainStringSchema(ir: { k: string }): ir is { k: "string" } {
return ir.k === "string";
} Try / catch
try {
schema = omptype.record(keySchema, valueSchema);
} catch (err) {
if (err instanceof OmpTypeError && err.message.includes("record keys")) {
throw new Error("use t.string() for record keys; use t.object() for fixed/enum keys");
} else throw err;
} Prevention
- Unlike Zod, omptype record keys must be t.string(); use t.object({...}) when keys are a fixed set
- Do not wrap the key schema in modifiers (enum, max, transforms) before passing it to record()
- When porting z.record(z.enum([...]), v), rewrite as an object schema with the enum members as props
When it happens
Trigger: `omptype.record(t.number(), value)`; `omptype.record(omptype.enum(["a","b"]), value)`; `omptype.record(t.string().max(3), value)` if the bound makes the key IR something other than a plain string schema accepted by isStringKeyIR.
Common situations: Porting Zod code where z.record(z.enum([...]), v) is legal but omptype restricts record keys to plain strings; wanting constrained keys — model those as t.object({...}) with named props instead; using t.literal(...) as key schema.
Related errors
- Record key must be assignable to string or symbol
- number max must not be NaN
- cannot apply max to ${ir.k}
- cannot apply int to ${schema.ir.k}
- cannot apply positive to ${schema.ir.k}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/a93a90fe5f905d79.
Report an issue: GitHub.