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:
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.
import { NodeFileSystem } from "@effect/platform-node"import { Effect, FileSystem, Glob } from "effect"
// Match paths without touching the file systemconst sources = Glob.make("src/**/*.{ts,tsx}")
Glob.match(sources, "src/ui/Button.tsx") // => trueGlob.match(sources, "src/.cache/index.ts") // => false
// Or search the file system with the same pattern syntaxconst 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:
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 Problem Details, using
application/problem+json. Declare a typed problem with
HttpApiError.Problem and use it as an endpoint’s error schema:
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/CommandLinequotes and joins command lines.ChildProcessacceptsRedactedarguments and environment values, hiding them in errors. UseCommandLine.displayfor logs;joinandquoteinclude secrets because their output is meant to run. - AI decisions:
Decision.Imagedeclares images in the input schema, andDecision.locatereturns 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.visibilityto hide tools, resources and prompts for that request.McpTest.makeClienttests registrations in memory;cacheHintscontrols discovery caching for clients on the2026-07-28protocol revision. - Pool metrics:
Pool.stats,PgPool.statsandPgClient.poolStatsreport idle items, used items and waiting borrowers. - Durations:
Duration.fromIso,Duration.formatIsoandSchema.DurationFromIsoStringsupport ISO 8601 durations with fixed-length units (weeks through seconds, not months or years). - Pattern matching:
Match.literalsandMatch.literalsExhaustivematch string or number literal unions with a record of handlers. - Failure logging:
Effect.ignore({ log: "Unhandled" })uses the configuredUnhandledLogLevel. Unobserved release failures inRcMap,RcRefandScopedCacheare 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 before upgrading.
Crypto
- Use
SecureRandomfor randomness and UUIDs, andDigestfor hashing.effect/testing/TestCryptohas been removed; useLayer.mockfor the services you need instead. - Crypto is no longer included in the runtime
*Servicesbundles, so it has to be provided explicitly. SqlMessageStorageandSingleRunnernow requireDigest.Digestinstead ofCrypto.Crypto.SqlEventLogServerEncrypted.makeStorageandlayerStoragerequireDigest.Digestinstead ofEventLogEncryption.EventLogEncryption.sha256Stringhas been removed. Usedigest.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.
Child processes
ChildProcess.maketemplates throw aCommandLineErrorfor shell syntax such as|,$HOMEor*. Quote those characters or pass them with${}.- With the
shelloption, templates cannot contain${}values, and extra arguments fail withBadArgument. Pass the whole script as the command, built withCommandLine.join, and name a POSIX shell: on Windows,shell: truerunscmd.exe, which noCommandLineformat quotes safely. ChildProcess.TemplateExpressionandChildProcess.TemplateExpressionItemhave been removed. Use theCommandLinetypes of the same name.
File systems, AI and MCP
- Custom
FileSystemimplementations must providereadDirectoryEntries.FileSystem.makederivesglobfor you. DecideOptions.imageshas been removed. AddDecision.Imagefields to your input schema and refer to each image by its path in your instructions.- Locate decisions on
TypeSafeDecisionModelrequire Jev 1.14 (jev-previeworjev-1.14.0). - MCP registration layers now go through the transport’s
registrationsoption. UseLayer.unwrapinstead ofLayer.providefor handler services captured at construction time. @effect/ai-anthropichas breaking changes to Files and Skills client methods and file uploads; see its CHANGELOG for details.
Persistence, networking and other API changes
FetchHttpClientfails withEncodeErroron runtimes without streaming request-body support. SetFetchHttpClient.StreamBodyFallbackto"buffer"to buffer the body instead.- Custom CLI help formatters should read ancestor flags from
HelpDoc.inheritedFlags. Persistencestores onKeyValueStoreand Redis are now isolated from each other, using<storeId>:<key>keys. WithPersistence.layerKvs, entries written by earlier versions are no longer read, so cached results are recomputed andChat.layerPersistedloses its stored histories. Redis keys only change for store ids containing%or:.KeyValueStore.clearPrefixhas been added.- Redis
PersistedQueuelocks 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: truenow complete when the server acknowledges them.SchemaBinarypeers have to be upgraded together. Toml.parsereturns aResultinstead of throwing.sql.insertandsql.updateValuesrequire a non-empty array of rows.Migrator.fromFileSystemhas moved toeffect/sql/MigratorFileSystem, soeffect/sqlcan be imported under Metro in React Native.- Received and forwarded HTTP request methods are typed as
string. UseHttpMethod.isHttpMethodto narrow them. Atom.Serializableencodes to and decodes fromSchema.Json.- Node stream and socket pulls are bounded by
chunkSize(default 4096). If you usedchunkSizeas a byte read size, use the newreadSizeoption. HttpClient.withCookiesReftakes aRef<ReadonlyArray<Cookie>>, and only sends stored cookies to a matching host, path and scheme. Seed the ref withCookies.mergeIntoArray, or with cookies that set adomain.BunHttpServerdisables Bun’s 10 secondidleTimeoutunless you set one, so quiet response streams and slow uploads are no longer cut off. Servers exposed directly to the internet should setidleTimeout.@effect/opentelemetryno longer re-exportsNodeSdkandWebSdkfrom the package root. Import them from@effect/opentelemetry/NodeSdkand@effect/opentelemetry/WebSdk.- Custom terminals passed to
Terminal.makehave to providesize. McpServermiddleware that wraps a visibility runs on list requests, soMiddlewareMethodincludes the list methods. Middleware provided both locally and globally now runs once. Unmet per-request services are nowMcpRequest<"Requires", S>layer requirements, replacingMissingRequestDependencies.- Custom stores built with
KeyValueStore.makeorKeyValueStore.makeStringOnlyhave to providekeys.sizeandisEmptyonKeyValueStore.prefix(...)now only count keys under the prefix. HttpApiClient.Upgradedvalues carry a[HttpApiClient.UpgradedTypeId]brand, so hand-built values such as client mocks have to add it.AiError.HttpRequestDetailsandResponse.HttpRequestDetailshave been removed, so AI errors and response metadata no longer carry request headers or query parameters.AiError.NetworkErrorhasmethodandurlfields instead ofrequest.
For all additions and fixes, see the CHANGELOG.
Join our Discord Community to follow the latest updates and discuss the release!