{"record":{"id":"a93a90fe5f905d79","repo":"can1357/oh-my-pi","slug":"record-keys-must-use-a-string-schema","errorCode":null,"errorMessage":"record keys must use a string schema","messagePattern":"record keys must use a string schema","errorType":"exception","errorClass":"OmpTypeError","httpStatus":null,"severity":"error","filePath":"packages/omptype/src/zod.ts","lineNumber":310,"sourceCode":"\treturn decorate(schemaFromIR<Values[number]>(type.enumerated(...values).ir));\n};\n\nexport { enumSchema as enum };\nexport const union = <\n\tconst Schemas extends readonly [ZodLikeSchema<unknown>, ZodLikeSchema<unknown>, ...ZodLikeSchema<unknown>[]],\n>(\n\tschemas: Schemas,\n): ZodLikeSchema<UnionOutput<Schemas>> =>\n\tdecorate(schemaFromIR({ k: \"union\", members: schemas.map(schema => embed(schema)) }));\nexport const array = <Element>(element: ZodLikeSchema<Element>): ZodLikeSchema<Element[]> =>\n\tdecorate(schemaFromIR({ k: \"array\", el: embed(element) }));\nexport const object = <const S extends Shape>(shape: S): ZodLikeSchema<Simplify<ObjectOutput<S>>> =>\n\tobjectSchema(shape);\nexport const record = <Key extends string, Value>(\n\tkeySchema: ZodLikeSchema<Key>,\n\tvalueSchema: ZodLikeSchema<Value>,\n): ZodLikeSchema<Record<string, Value>> => {\n\tif (!isStringKeyIR(keySchema.ir)) throw new OmpTypeError(\"record keys must use a string schema\");\n\tconst base = schemaFromIR<Record<string, Value>>({\n\t\tk: \"object\",\n\t\tprops: [],\n\t\tindex: embed(valueSchema),\n\t\textras: \"keep\",\n\t});\n\tconst checked = base.narrow((value, ctx: NarrowContext) => {\n\t\tfor (const key in value) {\n\t\t\tif (keySchema(key) instanceof type.errors) return ctx.mustBe(\"a record with valid string keys\");\n\t\t}\n\t\treturn true;\n\t});\n\treturn decorate(checked);\n};\nexport const unknown = (): ZodLikeSchema<unknown> => decorate(schemaFromIR(type.unknown.ir));\nexport const any = (): ZodLikeSchema<unknown> => decorate(schemaFromIR(type.unknown.ir));\nconst nullSchema = (): ZodLikeSchema<null> => decorate(type.raw(\"null\") as unknown as Decoratable<null>);\nconst undefinedSchema = (): ZodLikeSchema<undefined> =>","sourceCodeStart":292,"sourceCodeEnd":328,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/omptype/src/zod.ts#L292-L328","documentation":"`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.","triggerScenarios":"`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.","commonSituations":"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.","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"],"exampleFix":"// before\nomptype.record(omptype.enum([\"a\", \"b\"]), t.number())\n// after\nomptype.record(t.string(), t.number())\n// or for fixed keys:\nomptype.object({ a: t.number(), b: t.number() })","handlingStrategy":"type-guard","validationCode":"function hasPlainStringKeys(schema: ZodLikeSchema<unknown>): boolean {\n  return schema.ir.k === \"string\";\n}\nif (!hasPlainStringKeys(keySchema)) throw new Error(\"record key schema must be t.string()\");","typeGuard":"function isPlainStringSchema(ir: { k: string }): ir is { k: \"string\" } {\n  return ir.k === \"string\";\n}","tryCatchPattern":"try {\n  schema = omptype.record(keySchema, valueSchema);\n} catch (err) {\n  if (err instanceof OmpTypeError && err.message.includes(\"record keys\")) {\n    throw new Error(\"use t.string() for record keys; use t.object() for fixed/enum keys\");\n  } else throw err;\n}","preventionTips":["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"],"tags":["schema-builder","record","keys"],"backgroundTag":"record-key-schema-invalid","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}