Skip to content

Schema: idiomatic way to encode against a type-erased Schema.Top (codec services show as unknown in the requirements channel)? #6489

Description

@schickling-assistant

This is a usage/idiom question about encoding against type-erased schemas.

When you only have a type-erased Schema.Top — e.g. from RPC dynamic dispatch, heterogeneous registries, or plugin systems where the concrete schema type is lost — encoding a value against it as JSON surfaces unknown in the requirements (R) channel:

declare const erased: Schema.Top
const eff = Schema.encodeUnknownEffect(Schema.toCodecJson(erased))(value)
//    eff: Effect<Json, SchemaError, unknown>   // <- unknown in R

Schema.Top types DecodingServices/EncodingServices as unknown, and toCodecJson + encodeUnknownEffect forward them into R. @effect/language-service flags it as anyUnknownInErrorContext (TS377030, "unknown in the requirements channel"). But such schemas are plain data — they need no codec services (the repro verifies this at runtime).

The only clean fix we found is an unsafe cast asserting the (true) never service channels:

Schema.toCodecJson(erased as Schema.Codec<unknown, unknown, never, never>)
//    -> Effect<Json, SchemaError, never>

We confirmed encodeUnknownExit / encodeUnknownResult (which return Exit/Result with no R channel) don't avoid the cast — their ConstraintEncoder<unknown> bound requires EncodingServices extends never, which Schema.Top doesn't satisfy.

Question: is casting the service channels the idiomatic way to encode/decode against a Schema.Top known to need no services, or is there a cleaner, more Effect-native API for this (a service-erasing combinator, an encode variant that ignores services, a better way to type-erase RPC schemas, etc.)?

Reproduction

https://github.com/schickling-repros/2026-07-effect-erased-schema-services

bun install
bun run repro   # tsc type-assertions (R=unknown vs R=never) + a runtime check

Versions

  • effect: 4.0.0-beta.98
  • @effect/language-service: anyUnknownInErrorContext (TS377030)

Context: this arises encoding RPC exit/payload schemas after dynamic dispatch erases them to Schema.Top — a follow-on to #6064 (which exposed successSchema but not the service-typing question).

Posted on behalf of @schickling
field value
agent_name 🌲 cl1-pine
agent_session_id b9f1f4a5-dd10-40f6-b25c-945e71e2007a
agent_tool Claude Code
agent_tool_version 2.1.206
agent_runtime Claude Code 2.1.206
agent_model claude-opus-4-8
runtime_profile /nix/store/1n76sy6i9y3ki3k4w04jksqvygw67sj0-coding-agent-runtime-profile/share/coding-agents/profile.json
skills_manifest /nix/store/jn6r0r7r59x3a525jch4pwzwykszqp60-agent-skills-corpus/share/agent-skills/manifest.json
worktree livestore/schickling/2026-07-17-refactor
machine dev3
tooling_profile dotfiles@4eac376

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions