## Summary
`materializeCodec` evaluated `descriptor.factory` to a bare value before
calling it, so `this` was `undefined` inside the factory. Every codec
built as `new XCodec(this)` — the standard pattern for class-based
codecs — received `descriptor === undefined`.
`CodecImpl.get id()` is `this.descriptor.codecId`, so `codec.id` threw
on every such codec.
## Why it went unnoticed
`id` is read almost nowhere: the decode and encode failure wrappers read
it to build their message, and little else does. So the fault only
surfaced on an already-failing path — and when it did, it replaced a
diagnostic that names the column with one that names nothing:
```
TypeError: Cannot read properties of undefined (reading 'codecId')
```
The wrapper's real message — `Failed to decode column <table>.<column>
with codec '<id>'` — already existed and never got the chance to render.
Both `decoding.ts` and `encoding.ts` were affected.
## The change
```diff
- return blindCast<
- (params: unknown) => (ctx: CodecInstanceContext) => Codec,
- 'registry erases P to any; paramsSchema validates input before forwarding'
- >(descriptor.factory)(validated)(ctx);
+ return descriptor.factory(validated)(ctx);
```
The `blindCast` is not needed once the call is a method call:
`AnyCodecDescriptor` is `CodecDescriptor<any>`, so the validated params
pass without narrowing. Net one fewer cast against the ratchet.
## Blast radius
`this.descriptor` is dereferenced at four production sites
(`codec.ts:73`, the postgres and sqlite codec descriptors). None
branches or memoises on it, so nothing depended on the `undefined`.
Closure-style factories were already correct —
`PostgresCodecDescriptorAdapter` assigns `this.factory = (params) =>
descriptor.factory(params)`, an arrow that forwards regardless of
receiver. So adapted extension descriptors (pgvector, postgis,
arktype-json) were unaffected; only directly-declared
`CodecDescriptorImpl` subclasses were broken.
## Testing
`materialize-codec.test.ts` covers a non-parameterized and a
parameterized descriptor whose factories use `new XCodec(this)`, and
asserts `codec.id`. Verified load-bearing: against `main`'s version 2 of
the 3 cases fail with the exact `Cannot read properties of undefined
(reading 'codecId')` above.
- `@internal/framework-components`: 54 files, 628 tests
- Codec consumers: `@internal/sql-runtime` 343,
`@internal/target-postgres` 1596, `@internal/adapter-postgres` 867 + 3
expected-fail
- Repo `pnpm typecheck`: 166/166; package lint clean
## Provenance
Salvaged from #30195, which was closed. That PR paired this fix with an
interim `::text[]` projection cast for enum-array decoding; the cast is
superseded by target-owned list framing, but this defect is unrelated to
framing and is worth landing on its own.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01UcqoY3CKfnubdZt5YQk2Rq
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Fixed codec creation so descriptor-bound factories retain the correct
context.
* Ensured both standard and parameterized codec references resolve
correctly, including encoding and decoding behavior.
* **Tests**
* Added coverage for codec materialization with non-parameterized and
parameterized codecs.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Signed-off-by: Steven McClankerton <tatarintsev@prisma.io>
Co-authored-by: Steven McClankerton <tatarintsev@prisma.io>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>