bug(JsonSchema): boolean schema 'false' is destructured into empty object schema {}
Problem
According to the JSON Schema spec (Draft 2020-12 §4.3.2 / Draft-07 §3.2), a boolean schema false should reject every input value (it is unsatisfiable, equivalent to Schema.Never).
However, right now if you pass false to JsonSchema.fromSchemaDraft2020_12(false) (or fromSchemaDraft07 / fromSchemaOpenApi3_1), it silently turns into {} (an empty schema that accepts everything).
Step-by-step Reproduction & Root Cause
I stepped through the source code with the debugger, and here is exactly what happens:
1. Entry point: rest destructuring on a boolean turns it into {}
In packages/effect/src/JsonSchema.ts line 320:
export function fromSchemaDraft2020_12(js: JsonSchema): Document<"draft-2020-12"> {
const { $defs, ...schema } = js
return {
dialect: "draft-2020-12",
schema,
definitions: Predicate.isObject($defs) ? ($defs as Definitions) : {}
}
}When js is false, running const { $defs, ...schema } = false evaluates schema to {} (empty object).
As seen in the debugger above, js is false, but after line 320, schema becomes {}. The document returned is { dialect: "draft-2020-12", schema: {}, definitions: {} }.
2. Downstream consumer: skips if (input === false) and accepts everything
The downstream parser in packages/effect/src/internal/schema/fromJsonSchemaDocument.ts line 868 already intends to support false:
function translateSchema(input: unknown, path: Path, isRoot = false): ImportedJsonSchemaRepresentation {
if (input === false) {
return never
}
if (!isObject(input)) return unknown
...However, because the document's schema was already flattened into {} by fromSchemaDraft2020_12, translateSchema receives input = {}:
As shown in the screenshot, input is {} so input === false evaluates to false. The return never branch is completely missed, and it falls through to generic object handling, turning it into a type that accepts any input.
Minimal Reproducible Example
import { JsonSchema, Schema, SchemaRepresentation } from "effect"
// 1. Raw boolean schema: false (should reject everything)
const raw = false
// 2. Convert to document
const doc = JsonSchema.fromSchemaDraft2020_12(raw)
console.log(doc.schema) // Output: {} (Expected: false)
// 3. Convert back to Effect Schema
const effectSchema = SchemaRepresentation.fromJsonSchemaDocument(doc)
// 4. Test validation with any random string
console.log(Schema.is(effectSchema)("hello world"))
// Actual: true (Accepts anything!)
// Expected: false (Schema.Never should reject everything)Proposed Fix
Handle boolean schema upfront before rest destructuring in packages/effect/src/JsonSchema.ts:
export function fromSchemaDraft2020_12(js: JsonSchema): Document<"draft-2020-12"> {
+ if (typeof js === "boolean") {
+ return {
+ dialect: "draft-2020-12",
+ schema: js,
+ definitions: {}
+ }
+ }
const { $defs, ...schema } = js
return {
dialect: "draft-2020-12",
schema,
definitions: Predicate.isObject($defs) ? ($defs as Definitions) : {}
}
}Source: Effect-TS/effect