Decoding TypeScript Error Messages
Effect’s type system produces complex error messages. Here’s how to read them.
Common Error Catalog
| Error Message | Cause | Solution |
|---|---|---|
Type 'X' is not assignable to 'Effect<...>' | Missing yield* or wrong return type in a generator | Add yield* before the effect, or return yield* for terminal effects |
Context Service not found: myapp/Service | Missing Effect.provide(Layer) for a required service | Provide the layer: program.pipe(Effect.provide(MyService.layer)) |
Cannot yield a value of type 'X' | The value is not a Yieldable type | Use explicit methods: Ref.get(ref), Deferred.await(d), Fiber.join(f) instead of yielding directly |
Type 'Effect<A, E, R>' is not assignable to 'Effect<A, E, never>' | The R channel still has unmet dependencies | Provide all required layers until R becomes never |
Property 'catchAll' does not exist on type 'Effect' | Using v3 API name in v4 | Rename to Effect.catch |
Type 'Option<A>' is not assignable to 'Effect<A, ...>' | Using v3 subtyping in v4 where Option is no longer an Effect | Use .asEffect() or yield* in a generator |
Property 'Default' does not exist on type 'typeof MyService' | Using v3 Effect.Service auto-generated .Default layer | Define layers explicitly: static readonly layer = Layer.effect(this, this.make) |
Expected 1 type arguments, but got 2 on Context.Service | Using v3 Context.Tag(id)<Self, Shape>() syntax | Use v4 syntax: Context.Service<Self, Shape>()(id) |
Property 'Tag' does not exist on type 'typeof Context' | Using v3 Context.Tag in v4 | Use Context.Service<Self, Shape>()(id) |
”Type X is not assignable to Effect<…>”
// Error: Type 'number' is not assignable to 'Effect<number, never, never>'
Effect.gen(function*() {
const result = 42 // forgot to wrap in an effect
return result
})
// Fix: wrap values in Effect.succeed, or yield* the effect directly
Effect.gen(function*() {
return yield* Effect.succeed(42)
})
This usually means you returned a plain value where an Effect was expected, or you forgot yield* before an effectful computation.
”Service not found” / “Context not provided”
// Runtime error: Context Service not found: myapp/Database
const program = Effect.gen(function*() {
const db = yield* Database
return yield* db.query("SELECT 1")
})
// Missing: Effect.provide(Database.layer)
Effect.runPromise(program) // crashes
// Fix
Effect.runPromise(program.pipe(Effect.provide(Database.layer)))
This is a runtime error, not a compile error. The service identifier in the message (e.g., myapp/Database) tells you which layer to provide.
Gotcha:
Effect.runPromiseacceptsEffect<unknown, unknown, never>, so TypeScript won’t always catch missing layers at compile time. If yourRchannel is notnever, you still have unmet dependencies.
”Cannot yield a value of type X”
// v4 error: Cannot yield a value of type 'Ref<number>'
Effect.gen(function*() {
const ref = yield* Ref.make(0)
const value = yield* ref // ref is not Yieldable in v4
})
// Fix: use explicit method
Effect.gen(function*() {
const ref = yield* Ref.make(0)
const value = yield* Ref.get(ref)
})
In v4, Ref, Deferred, and Fiber no longer implement Yieldable. They are plain runtime values. You must call their module methods explicitly.
| Type | v3 (yieldable) | v4 (explicit) |
|---|---|---|
Ref<A> | yield* ref | yield* Ref.get(ref) |
Deferred<A, E> | yield* deferred | yield* Deferred.await(deferred) |
Fiber<A, E> | yield* fiber | yield* Fiber.join(fiber) |
Type Mismatch on R Channel
// Error: Effect<User, DbError, Database> is not assignable to Effect<User, DbError, never>
const getUser = Effect.gen(function*() {
const db = yield* Database
return yield* db.query("SELECT * FROM users WHERE id = 1")
})
// R = Database, not never. You must provide it.
Effect.runPromise(getUser) // type error
// Fix
Effect.runPromise(getUser.pipe(Effect.provide(Database.layer)))
The R channel is Effect’s dependency tracker. R = never means all dependencies are satisfied. Any other R value means you need to provide more layers.
”Effect is not assignable to Effect” Variance Issues
// Error: Effect<A, E1, R> is not assignable to Effect<A, E2, R>
// where E1 is a broader union than E2
// Cause: you're returning an effect with more error types than declared
const fn = (n: number): Effect.Effect<number, ParseError> => {
if (n < 0) {
return yield* new ParseError({ input: String(n) }) // also returns RangeError in union
}
return Effect.succeed(n)
}
// Fix: include all possible error types in the signature
const fn = (n: number): Effect.Effect<number, ParseError | RangeError> => {
// ...
}
Effect’s E channel is contravariant in some contexts. If your function can produce multiple error types, all of them must appear in the type signature.
v3 to v4 Migration Type Errors
| v3 Code | v4 Error | Fix |
|---|---|---|
import { Either } from "effect" | Module not found | import { Result } from "effect" |
Context.Tag("X")<X, Shape>() | Wrong type argument count | Context.Service<X, Shape>()("X") |
Effect.Service<X>()("X", { effect }) | Property doesn’t exist | Context.Service<X>()("X", { make }) + explicit Layer.effect |
Effect.catchAll(...) | Property doesn’t exist | Effect.catch(...) |
effect/TMap | Module not found | effect/TxHashMap |
effect/TestClock | Module not found | effect/testing/TestClock |
@effect/platform/HttpClient | Package not found | effect/unstable/http/HttpClient |
See v3 to v4 Migration for the full import map.