Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
264 changes: 264 additions & 0 deletions apps/web/src/content/blog/releases/effect/4.1.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
---
pagefind: false
title: Effect 4.1 (Release)
excerpt: New Effect release featuring the effect/crypto modules, Glob module, streaming CSV, HttpApi Problem Details, Decision.locate, and more!
date: 2026-10-20
authors:
- tim_smart
tags:
- releases
- effect
---

Effect 4.1 has been released! This release includes a number of new features
and improvements. Here's a summary of what's new:

## Crypto services

The new `effect/crypto` modules cover randomness, hashing, encryption,
signing and key management. Provide the services explicitly with a platform
layer such as `NodeCrypto.layer`:

```ts
import { NodeCrypto } from "@effect/platform-node"
import { Effect } from "effect"
import { Digest, SecureRandom } from "effect/crypto"

const program = Effect.gen(function* () {
const random = yield* SecureRandom.SecureRandom
const digest = yield* Digest.Digest

const id = yield* random.uuidV7()
const checksum = yield* digest.digestHex(
Digest.sha256,
new TextEncoder().encode(id),
)

yield* Effect.log(id, checksum)
})

program.pipe(Effect.provide(NodeCrypto.layer), Effect.runFork)
```

These replace `effect/Crypto`. See the upgrade notes below before switching.

## Glob matching and file search

The experimental `Glob` module matches paths, and `FileSystem.glob` searches
for files with the same pattern syntax on Node, Bun and Deno.

```ts
import { NodeFileSystem } from "@effect/platform-node"
import { Effect, FileSystem, Glob } from "effect"

// Match paths without touching the file system
const sources = Glob.make("src/**/*.{ts,tsx}")

Glob.match(sources, "src/ui/Button.tsx") // => true
Glob.match(sources, "src/.cache/index.ts") // => false

// Or search the file system with the same pattern syntax
const program = Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem
const files = yield* fs.glob("src/**/*.ts", {
exclude: ["**/*.test.ts"],
})
yield* Effect.log(files)
})

program.pipe(Effect.provide(NodeFileSystem.layer), Effect.runFork)
```

Patterns use `/` as the separator. Results are relative to `root` unless the
pattern is absolute.

## Streaming CSV

An unstable `Csv` module has been added to `effect/encoding`, for parsing and
writing CSV as a stream of text or UTF-8 bytes. It supports header records and
can convert cells using a `Schema`:

```ts
import { Effect, Schema, Stream } from "effect"
import { Csv } from "effect/encoding"

const Person = Schema.Struct({
name: Schema.String,
age: Schema.Number,
}).pipe(Schema.encodeKeys({ name: "Full Name", age: "Age (years)" }))

const people = Stream.make("Full Name,Age (years)\nAlice,32\nBob,41\n").pipe(
Stream.pipeThroughChannel(Csv.decodeSchemaString(Person)({ headers: true })),
Stream.runCollect,
)

Effect.runPromise(people).then(console.log)
// [{ name: "Alice", age: 32 }, { name: "Bob", age: 41 }]
```

## HttpApi Problem Details

`HttpApi` can now render, document and decode errors as
[RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) Problem Details, using
`application/problem+json`. Declare a typed problem with
`HttpApiError.Problem` and use it as an endpoint's error schema:

```ts
import { Schema } from "effect"
import { HttpApiError } from "effect/http-api"

class OutOfCredit extends HttpApiError.Problem<OutOfCredit>()(
"OutOfCredit",
{
type: "https://example.com/probs/out-of-credit",
status: 403,
title: "You do not have enough credit.",
},
{ balance: Schema.Number },
) {}
```

Built-in errors can opt in with `HttpApiSchema.asProblem`. Add
`HttpApiMiddleware.ProblemDetails` and its layer to render request decoding
failures as `400` problems and response encoding failures as `500` problems.
Nothing changes unless you opt in.

## More highlights

- **Child processes:** `effect/process/CommandLine` quotes and joins command
lines. `ChildProcess` accepts `Redacted` arguments and environment values,
hiding them in errors. Use `CommandLine.display` for logs; `join` and
`quote` include secrets because their output is meant to run.
- **AI decisions:** `Decision.Image` declares images in the input schema, and
`Decision.locate` returns a bounding box around a target. Providers must
opt in to locate support.
- **MCP servers:** supply handler services and middleware per request, and use
`McpServer.visibility` to hide tools, resources and prompts for that request.
`McpTest.makeClient` tests registrations in memory; `cacheHints` controls
discovery caching for clients on the `2026-07-28` protocol revision.
- **Pool metrics:** `Pool.stats`, `PgPool.stats` and `PgClient.poolStats`
report idle items, used items and waiting borrowers.
- **Durations:** `Duration.fromIso`, `Duration.formatIso` and
`Schema.DurationFromIsoString` support ISO 8601 durations with fixed-length
units (weeks through seconds, not months or years).
- **Pattern matching:** `Match.literals` and `Match.literalsExhaustive`
match string or number literal unions with a record of handlers.
- **Failure logging:** `Effect.ignore({ log: "Unhandled" })` uses the configured
`UnhandledLogLevel`. Unobserved release failures in `RcMap`, `RcRef` and
`ScopedCache` are now reported at that level too.

## Upgrade notes

This release includes breaking changes. Expand the notes for the APIs you use,
and check the [full CHANGELOG](https://github.com/Effect-TS/effect/blob/main/packages/effect/CHANGELOG.md)
before upgrading.

<details>
<summary>Crypto</summary>

- Use `SecureRandom` for randomness and UUIDs, and `Digest` for hashing.
`effect/testing/TestCrypto` has been removed; use `Layer.mock` for the
services you need instead.
- Crypto is no longer included in the runtime `*Services` bundles, so it has
to be provided explicitly.
- `SqlMessageStorage` and `SingleRunner` now require `Digest.Digest` instead of
`Crypto.Crypto`. `SqlEventLogServerEncrypted.makeStorage` and `layerStorage`
require `Digest.Digest` instead of `EventLogEncryption`.
- `EventLogEncryption.sha256String` has been removed. Use
`digest.digestHex(Digest.sha256, bytes)`.
- Encoding secret or private keys through the byte / format schemas requires
`{ allowEncode: true }`.

Existing workflow IDs, activity idempotency keys, event log table names,
identities and encrypted batches are unchanged.

</details>

<details>
<summary>Child processes</summary>

- `ChildProcess.make` templates throw a `CommandLineError` for shell syntax
such as `|`, `$HOME` or `*`. Quote those characters or pass them with `${}`.
- With the `shell` option, templates cannot contain `${}` values, and extra
arguments fail with `BadArgument`. Pass the whole script as the command,
built with `CommandLine.join`, and name a POSIX shell: on Windows,
`shell: true` runs `cmd.exe`, which no `CommandLine` format quotes safely.
- `ChildProcess.TemplateExpression` and `ChildProcess.TemplateExpressionItem`
have been removed. Use the `CommandLine` types of the same name.

</details>

<details>
<summary>File systems, AI and MCP</summary>

- Custom `FileSystem` implementations must provide `readDirectoryEntries`.
`FileSystem.make` derives `glob` for you.
- `DecideOptions.images` has been removed. Add `Decision.Image` fields to
your input schema and refer to each image by its path in your instructions.
- Locate decisions on `TypeSafeDecisionModel` require Jev 1.14
(`jev-preview` or `jev-1.14.0`).
- MCP registration layers now go through the transport's `registrations`
option. Use `Layer.unwrap` instead of `Layer.provide` for handler services
captured at construction time.
- `@effect/ai-anthropic` has breaking changes to Files and Skills client
methods and file uploads; see its CHANGELOG for details.

</details>

<details>
<summary>Persistence, networking and other API changes</summary>

- `FetchHttpClient` fails with `EncodeError` on runtimes without streaming
request-body support. Set `FetchHttpClient.StreamBodyFallback` to `"buffer"`
to buffer the body instead.
- Custom CLI help formatters should read ancestor flags from
`HelpDoc.inheritedFlags`.
- `Persistence` stores on `KeyValueStore` and Redis are now isolated from each
other, using `<storeId>:<key>` keys. With `Persistence.layerKvs`, entries
written by earlier versions are no longer read, so cached results are
recomputed and `Chat.layerPersisted` loses its stored histories. Redis keys
only change for store ids containing `%` or `:`. `KeyValueStore.clearPrefix`
has been added.
- Redis `PersistedQueue` locks use a new format. Stop all old workers and let
their locks expire before starting upgraded workers on the same prefix.
- RPC calls with `discard: true` now complete when the server acknowledges
them. `SchemaBinary` peers have to be upgraded together.
- `Toml.parse` returns a `Result` instead of throwing.
- `sql.insert` and `sql.updateValues` require a non-empty array of rows.
- `Migrator.fromFileSystem` has moved to `effect/sql/MigratorFileSystem`, so
`effect/sql` can be imported under Metro in React Native.
- Received and forwarded HTTP request methods are typed as `string`. Use
`HttpMethod.isHttpMethod` to narrow them.
- `Atom.Serializable` encodes to and decodes from `Schema.Json`.
- Node stream and socket pulls are bounded by `chunkSize` (default 4096). If
you used `chunkSize` as a byte read size, use the new `readSize` option.
- `HttpClient.withCookiesRef` takes a `Ref<ReadonlyArray<Cookie>>`, and only
sends stored cookies to a matching host, path and scheme. Seed the ref with
`Cookies.mergeIntoArray`, or with cookies that set a `domain`.
- `BunHttpServer` disables Bun's 10 second `idleTimeout` unless you set one, so
quiet response streams and slow uploads are no longer cut off. Servers
exposed directly to the internet should set `idleTimeout`.
- `@effect/opentelemetry` no longer re-exports `NodeSdk` and `WebSdk` from the
package root. Import them from `@effect/opentelemetry/NodeSdk` and
`@effect/opentelemetry/WebSdk`.
- Custom terminals passed to `Terminal.make` have to provide `size`.
- `McpServer` middleware that wraps a visibility runs on list requests, so
`MiddlewareMethod` includes the list methods. Middleware provided both
locally and globally now runs once. Unmet per-request services are now
`McpRequest<"Requires", S>` layer requirements, replacing
`MissingRequestDependencies`.
- Custom stores built with `KeyValueStore.make` or
`KeyValueStore.makeStringOnly` have to provide `keys`. `size` and `isEmpty`
on `KeyValueStore.prefix(...)` now only count keys under the prefix.
- `HttpApiClient.Upgraded` values carry a `[HttpApiClient.UpgradedTypeId]`
brand, so hand-built values such as client mocks have to add it.
- `AiError.HttpRequestDetails` and `Response.HttpRequestDetails` have been
removed, so AI errors and response metadata no longer carry request headers
or query parameters. `AiError.NetworkError` has `method` and `url` fields
instead of `request`.

</details>

For all additions and fixes, see the [CHANGELOG](https://github.com/Effect-TS/effect/blob/main/packages/effect/CHANGELOG.md).

Join our [Discord Community](https://discord.gg/effect-ts) to follow the latest updates and discuss the release!
Loading