Kysely: Proxy invariant violation: `effectifyWith` fails for non-configurable properties by johanneskares · Pull Request #5982 · Effect-TS/effect
Type
- Refactor
- Feature
- Bug Fix
- Optimization
- Documentation Update
Description
Bug Description
When using @effect/sql-kysely with Effect.fn, a TypeError is thrown due to JavaScript Proxy invariant violations. The error occurs because the effectifyWith proxy handler doesn't properly pass through non-configurable properties.
Error Messages
TypeError: 'get' on proxy: property 'Symbol(effect/Hash)' is a read-only and non-configurable data property on the proxy target but the proxy did not return its actual value
TypeError: 'get' on proxy: property 'node' is a read-only and non-configurable data property on the proxy target but the proxy did not return its actual value
Root Cause
The effectifyWith function in @effect/sql-kysely creates a Proxy that wraps Kysely query builders to make them Effect-compatible. However, the proxy's get handler violates a fundamental JavaScript Proxy invariant:
If the target object property is a non-configurable, non-writable data property, the proxy's
gettrap must return the same value as the target property.
The current implementation recursively wraps all property accesses, including:
- Symbol properties (like
Symbol(effect/Hash)) - Non-configurable properties (like
nodeon Kysely's internal AST)
When Effect.fn internally compares or hashes the returned value, it accesses these Symbol properties. The proxy returns a wrapped version instead of the actual value, triggering the invariant violation.
Reproduction
import { Effect } from "effect"; import { SqlKysely } from "@effect/sql-kysely"; // This works fine const workingQuery = Effect.gen(function* () { const db = yield* SqlKysely; return yield* db.selectFrom("users").selectAll(); }); // This fails with proxy invariant violation const brokenQuery = Effect.fn("brokenQuery")(function* () { const db = yield* SqlKysely; return yield* db.selectFrom("users").selectAll(); });
The bug only manifests when using Effect.fn because it performs internal operations (like hashing for tracing) that access Symbol properties on the returned Effect.
- Related Issue 3299