{"record":{"id":"89860667e700f015","repo":"can1357/oh-my-pi","slug":"json-schema-nodes-must-be-booleans-or-objects","errorCode":null,"errorMessage":"JSON Schema nodes must be booleans or objects","messagePattern":"JSON Schema nodes must be booleans or objects","errorType":"exception","errorClass":"OmpTypeError","httpStatus":null,"severity":"error","filePath":"packages/omptype/src/from-json-schema.ts","lineNumber":74,"sourceCode":"\t\t// to the same node instead of recursing forever.\n\t\tlet lowered: IR | undefined;\n\t\tconst alias: IR = {\n\t\t\tk: \"alias\",\n\t\t\tname: ref,\n\t\t\tresolve: () => {\n\t\t\t\tlowered ??= this.lower(target);\n\t\t\t\treturn lowered;\n\t\t\t},\n\t\t};\n\t\tthis.#aliases.set(ref, alias);\n\t\treturn alias;\n\t}\n\n\tlower(schema: unknown): IR {\n\t\tif (schema === true) return { k: \"unknown\" };\n\t\tif (schema === false) return { k: \"never\" };\n\t\tif (typeof schema !== \"object\" || schema === null) {\n\t\t\tthrow new OmpTypeError(\"JSON Schema nodes must be booleans or objects\");\n\t\t}\n\t\tconst node = schema as JsonSchema;\n\t\tconst desc = typeof node.description === \"string\" ? node.description : undefined;\n\t\tconst ir = this.#lowerNode(node);\n\t\treturn desc === undefined ? ir : { ...ir, desc };\n\t}\n\n\t#lowerNode(node: JsonSchema): IR {\n\t\tif (typeof node.$ref === \"string\") return this.resolveRef(node.$ref);\n\n\t\tif (Array.isArray(node.enum)) {\n\t\t\tconst members = node.enum.map((v): IR => ({ k: \"lit\", v }));\n\t\t\treturn members.length === 1 ? members[0] : { k: \"union\", members };\n\t\t}\n\t\tif (\"const\" in node) return { k: \"lit\", v: node.const };\n\n\t\tconst branches = node.anyOf ?? node.oneOf;\n\t\tif (Array.isArray(branches)) {","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/omptype/src/from-json-schema.ts#L56-L92","documentation":"Per JSON Schema spec, every schema node must be a boolean (`true`/`false`, meaning accept-anything/accept-nothing) or an object of keywords. `fromJsonSchema.lower()` throws when it encounters any other value — a string, number, array, or null — where a schema node was expected.","triggerScenarios":"Passing a schema where `properties` values, `items`, or the root are `null`, strings, or numbers; calling `lower(x)` directly with a non-schema value.","commonSituations":"JSON-parsed configs where `\"schema\": null`; OpenAPI-style schemas with `nullable: true` confused with a `null` node; hand-built schemas in JS that put non-schema values in keyword positions.","solutions":["Replace the invalid node with a real schema object, e.g. `{ \"type\": \"string\" }` or `true`.","Use `true`/`false` instead of null when you mean 'any' / 'never'.","For nullable values, use `{ \"type\": [\"string\", \"null\"] }` rather than a null node."],"exampleFix":"// before\n{ \"properties\": { \"name\": null } }\n// after\n{ \"properties\": { \"name\": { \"type\": [\"string\", \"null\"] } } }","handlingStrategy":"validation","validationCode":"function isSchemaNode(v: unknown): boolean {\n  return v === true || v === false || (typeof v === \"object\" && v !== null && !Array.isArray(v));\n}\n// apply to root, each properties value, items, etc.","typeGuard":"function isSchemaNode(v: unknown): v is boolean | Record<string, unknown> {\n  return v === true || v === false || (typeof v === \"object\" && v !== null && !Array.isArray(v));\n}","tryCatchPattern":null,"preventionTips":["Never place `null` where a schema node belongs; use `true`, `false`, or a typed schema object.","Model nullability with `{ \"type\": [\"T\", \"null\"] }`.","Type-check schema-building code against a `JsonSchema` type instead of `unknown`."],"tags":["json-schema","malformed-schema","type-error"],"backgroundTag":"malformed-json-schema","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}