From f0d29b273b579d98f537e7712b3ec47831e0845f Mon Sep 17 00:00:00 2001 From: Giulio Canti Date: Sat, 25 Jul 2026 22:28:39 +0200 Subject: [PATCH 1/2] Fix Schema JSON stringification of undefined --- packages/effect/src/SchemaGetter.ts | 13 ++++++++----- packages/effect/test/schema/SchemaGetter.test.ts | 9 +++++++-- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/packages/effect/src/SchemaGetter.ts b/packages/effect/src/SchemaGetter.ts index d1c70b05677..21cb1ffea27 100644 --- a/packages/effect/src/SchemaGetter.ts +++ b/packages/effect/src/SchemaGetter.ts @@ -1015,13 +1015,10 @@ type StringifyJsonOptions = { * **Details** * * - Skips `None` inputs. - * - On thrown stringify failures, such as circular references, fails with + * - If `JSON.stringify` throws or returns `undefined`, fails with * `SchemaIssue.InvalidValue`. * - Supports optional `replacer` and `space` options, matching * `JSON.stringify`. - * - If `JSON.stringify` returns `undefined`, such as for `undefined`, - * functions, symbols, or a replacer that removes the root value, that - * `undefined` result is returned rather than converted into an `Issue`. * * **Example** (Stringifying JSON) * @@ -1040,7 +1037,13 @@ type StringifyJsonOptions = { export function stringifyJson(options?: StringifyJsonOptions): Getter { return onSome((input) => Effect.try({ - try: () => Option.some(JSON.stringify(input, options?.replacer, options?.space)), + try: () => { + const output = JSON.stringify(input, options?.replacer, options?.space) + if (output === undefined) { + throw new TypeError("Value cannot be represented as JSON") + } + return Option.some(output) + }, catch: (e) => new SchemaIssue.InvalidValue(Option.some(input), { message: globalThis.String(e) }) }) ) diff --git a/packages/effect/test/schema/SchemaGetter.test.ts b/packages/effect/test/schema/SchemaGetter.test.ts index 00efce8921a..78e6a198b91 100644 --- a/packages/effect/test/schema/SchemaGetter.test.ts +++ b/packages/effect/test/schema/SchemaGetter.test.ts @@ -1,6 +1,5 @@ -import { assert } from "@effect/vitest" +import { assert, describe, it } from "@effect/vitest" import { DateTime, Effect, Option, Result, SchemaGetter } from "effect" -import { describe, it } from "vitest" import { assertSome, deepStrictEqual } from "../utils/assert.ts" function makeAsserts(getter: SchemaGetter.Getter) { @@ -16,6 +15,12 @@ function makeAsserts(getter: SchemaGetter.Getter) { } describe("SchemaGetter", () => { + it.effect("stringifyJson fails when JSON.stringify returns undefined", () => + SchemaGetter.stringifyJson().run(Option.some(undefined), {}).pipe( + Effect.flip, + Effect.asVoid + )) + it("map", () => { const getter = SchemaGetter.succeed(1).map((t) => t + 1) const result = Effect.runSync(getter.run(Option.some(1), {})) From abddba3f1cc4ce60abbed406d40a94406a293351 Mon Sep 17 00:00:00 2001 From: Giulio Canti Date: Sat, 25 Jul 2026 22:46:41 +0200 Subject: [PATCH 2/2] Assert Schema JSON stringify failure issue --- packages/effect/test/schema/SchemaGetter.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/effect/test/schema/SchemaGetter.test.ts b/packages/effect/test/schema/SchemaGetter.test.ts index 78e6a198b91..0e386148f08 100644 --- a/packages/effect/test/schema/SchemaGetter.test.ts +++ b/packages/effect/test/schema/SchemaGetter.test.ts @@ -18,7 +18,7 @@ describe("SchemaGetter", () => { it.effect("stringifyJson fails when JSON.stringify returns undefined", () => SchemaGetter.stringifyJson().run(Option.some(undefined), {}).pipe( Effect.flip, - Effect.asVoid + Effect.map((issue) => assert.strictEqual(issue._tag, "InvalidValue")) )) it("map", () => {