> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/Effect-TS/tsgo/llms.txt
> Use this file to discover all available pages before exploring further.

# Anti-pattern diagnostics

> Diagnostics that flag discouraged patterns in Effect code that often lead to bugs or confusing behavior.

Anti-pattern diagnostics highlight code that technically compiles but is considered problematic: it may cause subtle bugs, break service lifetimes, or obscure intent. Most are warnings or suggestions.

<Info>
  Rules marked ⚠️ are warnings by default. Rules marked 💡 are suggestions. Rules marked ➖ are off by default.
</Info>

<AccordionGroup>
  <Accordion title="catchUnfailableEffect — 💡 suggestion">
    **V3/V4:** ✓ / ✓

    Warns when error-handling operators like `Effect.catchAll`, `Effect.catch`, or `Effect.mapError` are applied to an Effect whose error channel is `never`. The handler can never be triggered, which signals a logic error or dead code.

    ```typescript theme={null}
    import { Effect } from "effect"

    // ❌ Violation — Effect.succeed never fails; catchAll is unreachable
    const result = Effect.succeed(42).pipe(
      Effect.catchAll((e) => Effect.succeed(0)) // never triggered
    )

    // ✅ Correct — only add error handling when the effect can fail
    const result = Effect.succeed(42)
    ```
  </Accordion>

  <Accordion title="effectFnIife — ⚠️ warning + 🔧 quick fix">
    **V3/V4:** ✓ / ✓

    `Effect.fn` and `Effect.fnUntraced` create reusable named functions. Calling them immediately as an IIFE (immediately invoked function expression) defeats that purpose — use `Effect.gen` directly instead.

    ```typescript theme={null}
    import { Effect } from "effect"

    // ❌ Violation — Effect.fn called immediately, not reused
    const result = Effect.fn("doWork")(function* () {
      return yield* Effect.succeed(42)
    })()

    // ✅ Correct — use Effect.gen when you don't need a reusable function
    const result = Effect.gen(function* () {
      return yield* Effect.succeed(42)
    })
    ```

    The quick fix replaces the `Effect.fn(...)()` call with `Effect.gen`.
  </Accordion>

  <Accordion title="effectGenUsesAdapter — ⚠️ warning">
    **V3/V4:** ✓ / ✓

    Warns when the deprecated first-parameter adapter (`_`) is used inside `Effect.gen`. The adapter was required in older versions of Effect but is now just an alias for `pipe` and should be removed.

    ```typescript theme={null}
    import { Effect } from "effect"

    // ❌ Violation — adapter parameter is no longer needed
    const program = Effect.gen(function* (_) {
      const value = yield* _(Effect.succeed(42))
      return value
    })

    // ✅ Correct — drop the adapter and use yield* directly
    const program = Effect.gen(function* () {
      const value = yield* Effect.succeed(42)
      return value
    })
    ```
  </Accordion>

  <Accordion title="effectInFailure — ⚠️ warning">
    **V3/V4:** ✓ / ✓

    Warns when an `Effect` value appears in the error channel (`E`) of another `Effect`. The failure channel is intended to hold failure *types* (plain data), not executable computations. An Effect in the failure channel is never run and suggests a design mistake.

    ```typescript theme={null}
    import { Effect } from "effect"

    // ❌ Violation — Effect in the error channel is never executed
    type Bad = Effect.Effect<number, Effect.Effect<string>, never>

    // ✅ Correct — use a plain error type in the failure channel
    class MyError extends Data.TaggedError("MyError")<{ message: string }> {}
    type Good = Effect.Effect<number, MyError, never>
    ```
  </Accordion>

  <Accordion title="effectInVoidSuccess — ⚠️ warning">
    **V3/V4:** ✓ / ✓

    Detects nested `Effect` values in a `void` success channel. When a function returns `void`, any Effect returned inside it is silently discarded — it will never be executed, creating a hard-to-spot floating effect.

    ```typescript theme={null}
    import { Effect } from "effect"

    // ❌ Violation — the inner Effect is silently discarded
    const program: Effect.Effect<void> = Effect.gen(function* () {
      // This Effect is created but never yielded
      return Effect.log("this never runs")
    })

    // ✅ Correct — yield* the effect to execute it
    const program = Effect.gen(function* () {
      yield* Effect.log("this runs")
    })
    ```
  </Accordion>

  <Accordion title="globalErrorInEffectCatch — ⚠️ warning">
    **V3/V4:** ✓ / ✓

    Warns when a catch callback (e.g., in `Effect.tryPromise`, `Effect.try`) returns the global `Error` type. Untagged errors merge together in the failure channel and lose type safety. Use a tagged error class instead.

    ```typescript theme={null}
    import { Effect } from "effect"

    // ❌ Violation — global Error loses type information
    const result = Effect.tryPromise({
      try: () => fetch("/api/users"),
      catch: (e) => new Error(String(e)) // global Error
    })

    // ✅ Correct — use a tagged error for type-safe error handling
    class FetchError extends Data.TaggedError("FetchError")<{ cause: unknown }> {}

    const result = Effect.tryPromise({
      try: () => fetch("/api/users"),
      catch: (e) => new FetchError({ cause: e })
    })
    ```
  </Accordion>

  <Accordion title="globalErrorInEffectFailure — ⚠️ warning">
    **V3/V4:** ✓ / ✓

    Warns when the global `Error` type appears in the failure channel of an `Effect` type annotation. Like `globalErrorInEffectCatch`, untagged errors are indistinguishable from each other and cannot be handled precisely.

    ```typescript theme={null}
    import { Effect, Data } from "effect"

    // ❌ Violation — global Error in the failure channel
    declare function loadConfig(): Effect.Effect<Config, Error>

    // ✅ Correct — use a discriminated tagged error
    class ConfigError extends Data.TaggedError("ConfigError")<{ message: string }> {}
    declare function loadConfig(): Effect.Effect<Config, ConfigError>
    ```
  </Accordion>

  <Accordion title="layerMergeAllWithDependencies — ⚠️ warning + 🔧 quick fix">
    **V3/V4:** ✓ / ✓

    Detects when one layer inside a `Layer.mergeAll` call provides a service that another layer in the same call requires. `Layer.mergeAll` builds layers in parallel, so inter-layer dependencies are not satisfied. Move the dependent layer into a `Layer.provideMerge` call after the `mergeAll`.

    ```typescript theme={null}
    import { Effect, Layer } from "effect"

    class Config extends Effect.Service<Config>()("Config", {
      effect: Effect.succeed({ url: "http://localhost" })
    }) {}

    class HttpClient extends Effect.Service<HttpClient>()("HttpClient", {
      effect: Effect.gen(function* () {
        const config = yield* Config
        return { get: () => Effect.void }
      })
    }) {}

    // ❌ Violation — HttpClient.Default requires Config, but both are in mergeAll
    const AppLayer = Layer.mergeAll(
      Config.Default,
      HttpClient.Default // depends on Config, but mergeAll is parallel
    )

    // ✅ Correct — provide Config before merging
    const AppLayer = Layer.provideMerge(HttpClient.Default, Config.Default)
    ```
  </Accordion>

  <Accordion title="leakingRequirements — 💡 suggestion">
    **V3/V4:** ✓ / ✓

    Detects implementation services (internal dependencies) that are leaked through the public API of a service's methods. When a service method requires an internal service from every caller, it exposes internal concerns to consumers. Resolve these dependencies at layer creation time instead.

    ```typescript theme={null}
    import { Effect } from "effect"

    class Logger extends Effect.Service<Logger>()("Logger", {
      effect: Effect.succeed({ log: (msg: string) => Effect.log(msg) })
    }) {}

    // ❌ Violation — Logger leaks into callers of UserService
    class UserService extends Effect.Service<UserService>()("UserService", {
      effect: Effect.succeed({
        getUser: (id: string) =>
          Effect.gen(function* () {
            const logger = yield* Logger // requirement leaks to callers
            yield* logger.log(`getting user ${id}`)
            return { id, name: "Alice" }
          })
      })
    }) {}

    // ✅ Correct — resolve Logger inside the layer
    class UserService extends Effect.Service<UserService>()("UserService", {
      effect: Effect.gen(function* () {
        const logger = yield* Logger
        return {
          getUser: (id: string) =>
            Effect.gen(function* () {
              yield* logger.log(`getting user ${id}`)
              return { id, name: "Alice" }
            })
        }
      })
    }) {}
    ```
  </Accordion>

  <Accordion title="multipleEffectProvide — ⚠️ warning + 🔧 quick fix">
    **V3/V4:** ✓ / ✓

    Warns when `Effect.provide` is chained multiple times on the same effect. Chaining `provide` can cause service scope and lifecycle issues because each call creates a separate scope. Compose all layers into one and provide them in a single call.

    ```typescript theme={null}
    import { Effect, Layer } from "effect"

    // ❌ Violation — multiple provide calls, separate scopes
    const program = myEffect.pipe(
      Effect.provide(DatabaseLayer),
      Effect.provide(LoggerLayer),
      Effect.provide(ConfigLayer)
    )

    // ✅ Correct — compose layers and provide once
    const AppLayer = Layer.mergeAll(DatabaseLayer, LoggerLayer, ConfigLayer)
    const program = myEffect.pipe(Effect.provide(AppLayer))
    ```
  </Accordion>

  <Accordion title="returnEffectInGen — 💡 suggestion + 🔧 quick fix">
    **V3/V4:** ✓ / ✓

    Warns when a `return` statement inside an `Effect.gen` generator returns an `Effect`-able value, resulting in a nested `Effect<Effect<...>>`. This is almost always unintentional — use `return yield*` to execute and unwrap the inner effect.

    ```typescript theme={null}
    import { Effect } from "effect"

    // ❌ Violation — returns Effect<number>, producing Effect<Effect<number>>
    const program = Effect.gen(function* () {
      const id = yield* getUserId()
      return fetchUser(id) // Effect<User>, not User
    })

    // ✅ Correct — yield* the inner effect to get User directly
    const program = Effect.gen(function* () {
      const id = yield* getUserId()
      return yield* fetchUser(id)
    })
    ```

    The quick fix adds `yield*` before the returned expression.
  </Accordion>

  <Accordion title="runEffectInsideEffect — 💡 suggestion + 🔧 quick fix (V3 only)">
    **V3 only**  ·  **V4:** —

    Suggests using `Runtime` methods instead of calling `Effect.runSync`, `Effect.runPromise`, or similar functions inside an Effect context. Inside a generator, effects can simply be yielded. When you need to run a child effect using a specific runtime, use `Effect.runtime` and then the corresponding `Runtime.*` method.

    ```typescript theme={null}
    import { Effect } from "effect"

    // ❌ Violation — Effect.runSync inside an Effect generator
    const program = Effect.gen(function* () {
      const result = Effect.runSync(someEffect) // anti-pattern
      return result
    })

    // ✅ Correct — yield* to execute inside the generator
    const program = Effect.gen(function* () {
      const result = yield* someEffect
      return result
    })
    ```
  </Accordion>

  <Accordion title="schemaSyncInEffect — 💡 suggestion (V3 only)">
    **V3 only**  ·  **V4:** —

    Suggests using Effect-based Schema methods (e.g., `Schema.decodeUnknown`) instead of the synchronous variants (e.g., `Schema.decodeUnknownSync`) inside Effect generators. The sync methods throw on failure, bypassing Effect's typed error channel. The Effect-based methods propagate errors through the channel with proper types.

    ```typescript theme={null}
    import { Effect, Schema } from "effect"

    const UserSchema = Schema.Struct({ id: Schema.String, name: Schema.String })

    // ❌ Violation — sync method throws, bypassing the error channel
    const program = Effect.gen(function* () {
      const user = Schema.decodeUnknownSync(UserSchema)(rawInput) // can throw
      return user
    })

    // ✅ Correct — Effect-based method surfaces errors in the error channel
    const program = Effect.gen(function* () {
      const user = yield* Schema.decodeUnknown(UserSchema)(rawInput)
      return user
    })
    ```
  </Accordion>

  <Accordion title="scopeInLayerEffect — ⚠️ warning + 🔧 quick fix (V3 only)">
    **V3 only**  ·  **V4:** —

    Suggests using `Layer.scoped` instead of `Layer.effect` when `Scope` appears in the layer's requirements channel. `Layer.scoped` is the correct constructor for layers that manage scoped resources; using `Layer.effect` leaves `Scope` in the requirements, forcing callers to supply it.

    ```typescript theme={null}
    import { Effect, Layer } from "effect"

    // ❌ Violation — Scope leaks into the layer requirements
    const ConnectionLayer = Layer.effect(
      Connection,
      Effect.gen(function* () {
        const scope = yield* Effect.scope
        // ... set up connection using scope
        return connection
      })
    )

    // ✅ Correct — Layer.scoped handles the Scope automatically
    const ConnectionLayer = Layer.scoped(
      Connection,
      Effect.gen(function* () {
        const conn = yield* Effect.acquireRelease(
          openConnection(),
          (conn) => closeConnection(conn)
        )
        return conn
      })
    )
    ```
  </Accordion>

  <Accordion title="strictEffectProvide — ➖ off by default">
    **V3/V4:** ✓ / ✓

    Warns when `Effect.provide` with a `Layer` is used outside of application entry points. Calling `Effect.provide` inside library code or service implementations can break scope lifetimes. All layers should be composed and provided once at the application entry point.

    This rule is off by default because there are valid use cases for `Effect.provide` in non-entry-point code (for example, test helpers). Enable it when you want to enforce strict layer discipline:

    ```jsonc theme={null}
    { "diagnosticSeverity": { "strictEffectProvide": "warning" } }
    ```

    ```typescript theme={null}
    import { Effect, Layer } from "effect"

    // ❌ Violation (when enabled) — provide inside a service method
    const makeUser = Effect.gen(function* () {
      const db = yield* Database
      return db.createUser()
    }).pipe(Effect.provide(Database.Default))

    // ✅ Correct — provide at the application entry point
    const program = makeUser.pipe(
      Effect.provide(AppLayer)
    )
    Effect.runPromise(program)
    ```
  </Accordion>

  <Accordion title="tryCatchInEffectGen — 💡 suggestion">
    **V3/V4:** ✓ / ✓

    Discourages the use of `try/catch` inside `Effect.gen` generators. Exception-based error handling bypasses Effect's typed error channel, making errors invisible to the type system. Use `Effect.try`, `Effect.tryPromise`, `Effect.catch`, or `Effect.catchTag` instead.

    ```typescript theme={null}
    import { Effect } from "effect"

    // ❌ Violation — try/catch bypasses the typed error channel
    const program = Effect.gen(function* () {
      try {
        const result = yield* riskyEffect
        return result
      } catch (e) {
        return defaultValue // error is untyped
      }
    })

    // ✅ Correct — use Effect's error handling mechanisms
    const program = riskyEffect.pipe(
      Effect.catchAll(() => Effect.succeed(defaultValue))
    )
    ```
  </Accordion>

  <Accordion title="unknownInEffectCatch — ⚠️ warning">
    **V3/V4:** ✓ / ✓

    Warns when a catch callback (e.g., in `Effect.tryPromise`, `Effect.try`) returns `unknown`. An `unknown` error type is too wide to handle precisely. Narrow the type or wrap the error in a tagged class to make it useful in the error channel.

    ```typescript theme={null}
    import { Effect, Data } from "effect"

    // ❌ Violation — catch returns unknown
    const result = Effect.tryPromise({
      try: () => fetch("/api"),
      catch: (e) => e // type is unknown
    })

    // ✅ Correct — wrap in a tagged error with context
    class FetchError extends Data.TaggedError("FetchError")<{ cause: unknown }> {}

    const result = Effect.tryPromise({
      try: () => fetch("/api"),
      catch: (e) => new FetchError({ cause: e })
    })
    ```
  </Accordion>
</AccordionGroup>
