github Effect-TS/effect effect@4.1.3

Patch Changes

  • #9990 56bbd6f Thanks @effect-bot! - Add Rational, an exact fraction type alongside BigDecimal, with Schema.Rational and Schema.RationalFromString.

    import { BigDecimal, Rational } from "effect"
    
    const third = Rational.makeUnsafe(1n, 3n)
    
    Rational.equals(Rational.multiply(third, Rational.fromBigInt(3n)), Rational.fromBigInt(1n)) // true
    Rational.toBigDecimal(third, { scale: 3 }) // BigDecimal 0.333
  • #9842 7150ea1 Thanks @tim-smart! - Add experimental AG-UI protocol support to effect/ai through the new AgUiSchema, AgUi, and AgUiServer modules, and a keepAlive option to HttpApiSchema.StreamSse.

  • #10377 da85361 Thanks @effect-bot! - Add CommandSpec, a lossless projection of an effect/cli command path for documentation tooling, plus Flag.annotate / Argument.annotate / Param.annotate for attaching custom metadata to parameters.

    import { Config, Context, Option } from "effect"
    import { Command, CommandSpec, Flag } from "effect/cli"
    
    const EnvVar = Context.Service<never, string>("app/EnvVar")
    
    const push = Command.make("push", {
      dbUrl: Flag.String("db-url").pipe(
        Flag.withFallbackConfig(Config.String("DB_URL")),
        Flag.annotate(EnvVar, "DB_URL")
      )
    })
    const cli = Command.make("app").pipe(Command.withSubcommands([push]))
    
    const spec = Option.getOrThrow(CommandSpec.resolve(cli, ["app", "push"]))
    for (const flag of spec.flags) {
      // flag.scope, flag.required, flag.fallbacks, Context.getOption(flag.annotations, EnvVar), ...
    }
  • #10417 95b01bb Thanks @effect-bot! - Fix Bash and Zsh CLI completions for subcommand names containing :, such as test:unit.

  • #10375 b09e7a8 Thanks @effect-bot! - Fix CLI completion descriptors marking variadic arguments without a positive minimum as required.

  • #10428 91386c0 Thanks @devangpratap! - Shell completions keep suggesting repeatable flags, such as Flag.atLeast and Flag.KeyValuePair, after their first use.

  • #10376 a2b1be0 Thanks @effect-bot! - Allow Command.validate to fail with your own errors, which Command.run reports like handler failures instead of showing help.

    Command.withHandler now keeps the command's existing error type, so errors from earlier validations are not lost. When it replaces a handler, the old handler's errors also stay in the type. Code that annotates such a command with a narrower error type must widen the annotation, or pass the final handler to Command.make instead.

  • #7322 1a2c5a6 Thanks @tim-smart! - Add experimental Cloudflare Cluster support with durable entities, workflows, queues, singletons, and optional Alchemy deployment, plus Effect hooks for keep-alive, durable clocks, and dead-letter detection.

  • #10311 2a9d989 Thanks @effect-bot! - Fix ClusterWorkflowEngine turning transient storage failures while starting a child workflow into a parent workflow defect; they are now retried.

  • #10356 66cbcca Thanks @Marve10s! - Fix transient storage read errors permanently failing ClusterWorkflowEngine workflows that await a DurableDeferred.

  • #10267 da52437 Thanks @effect-bot! - Add MessageRetention.make to delete a persisted entity's old completed requests, and fix @effect/sql-mysql2 not starting a transaction for a SET TRANSACTION BeginTransaction.

    • Custom MessageStorage implementations must add deleteProcessed.
    • SQL message storage on MySQL needs MySQL 8.0.1+ or MariaDB 10.6+, and binlog_format ROW or MIXED.
  • #10294 7e8f3fb Thanks @effect-bot! - Fix HttpMiddleware.cors calling the allowedOrigins predicate with undefined for requests without an Origin header.

  • #10456 1de4308 Thanks @Marve10s! - Fix Csv.decode and Csv.decodeString dropping records completed before a CsvError in the same input chunk.

  • #10439 cb36ec7 Thanks @jayhemnani9910! - Preserve the supplied time zone identifier in DateTime named zones, including aliases such as Europe/Kyiv or Etc/UTC, instead of the identifier Intl resolves them to (Europe/Kiev, UTC).

  • #10506 693259d Thanks @effect-bot! - Fix HttpServerRequest.stream failing on requests with no body; return an empty stream instead.

  • #10448 0daaa95 Thanks @effect-bot! - Speed up Base64.decode, Base64Url and Hex by using the native Uint8Array Base64 and hex methods when the runtime provides them. Base64Url.encode is also faster on runtimes without them.

  • #10350 f548e59 Thanks @asssslay! - Keep a leading U+FEFF when decoding strings with Base64, Base64Url, Hex, and their Schema.StringFrom* codecs, including HttpApiSecurity.basic usernames.

  • #10413 005d096 Thanks @effect-bot! - Fix Equal.equals and Hash.hash treating otherwise identical Error values as different on Bun when they were constructed at different source locations.

  • #10472 188c2b0 Thanks @effect-bot! - Fix Workflow.intoResult running workflow finalizers before handler cleanup completes when interrupted immediately after startup.

  • #10450 258fb50 Thanks @Marve10s! - Fix Headers.fromInput dropping earlier values for repeated header names in iterable input.

  • #10006 e799258 Thanks @tim-smart! - Redact HttpClientError request URLs with the UrlParams.CurrentRedactedNames of the fiber that executed the request and of the fiber that created the error, as well as those of the fiber that renders it.

  • #10496 b110d56 Thanks @darjss! - Add a message to HttpClientErrorSchema taken from its original reason, so RpcClientError HTTP failures no longer read as a bare HttpError:.

  • #10363 111615a Thanks @effect-bot! - HttpClient.withScope now owns every request sent while the scoped client executes, instead of tracking the request value.

  • #10366 4ed0fae Thanks @effect-bot! - Close a request scope transferred to a streamed body only after both the request and the body have finished, so middleware and tracing complete before request finalizers run.

  • #10332 21671d5 Thanks @matheuspuel! - Decode tool call parameters with their JSON codec, so a Schema.Date parameter accepts an ISO string from MCP clients and language models.

    • Toolkit.WithHandler.handle takes Schema.Json params. Direct callers pass the JSON encoding, with null instead of undefined for optional parameters.
    • With disableToolCallResolution: true, tool call params hold the JSON encoding and are typed Schema.Json. Provider-executed tools keep the provider's payload.
    • Provider-defined tools keep their provider-defined type through setSuccess, setFailure, setParameters, setNeedsApproval, addDependency, annotate and annotateMerge, so a tool the provider runs with a dependency is not mistaken for one that needs a handler.
  • #10438 2ad19ae Thanks @IfkumRfnl! - Defer formatting the source location of Atom.withLabel and Atom.serializable labels until it is read.

  • #10455 6235a2f Thanks @Marve10s! - Skip undefined handlers in Match.tags and Match.discriminators, and correct exhaustiveness checks for conditional handler maps, including Match.literals.

  • #10504 fe80e09 Thanks @itisrmk! - Fix MCP 2026-07-28 tools/call returning a header mismatch for tools hidden by McpServer.visibility, which revealed that they exist.

  • #10447 79af760 Thanks @nmnmcc! - Fix McpServer accepting cursors on core lists that never issue them, returning InvalidParams instead.

  • #10509 97f8120 Thanks @tim-smart! - Stop queued McpServer progress notifications after their request completes, is cancelled, or disconnects, while preserving background progress notifications.

  • #10398 c5383f8 Thanks @fubhy! - Fix McpServer.resource and McpServer.registerResource omitting the registered mimeType from resources/read contents when the handler returns a string or Uint8Array. The type was only listed by resources/list, so MCP Apps hosts could not render UI resources served as text/html;profile=mcp-app.

  • #10304 0d0c824 Thanks @effect-bot! - Trace McpServer requests with spans that follow the OpenTelemetry MCP semantic conventions.

    Spans are now named {method} {target} (for example tools/call get_weather) and carry mcp.*, jsonrpc.request.id and gen_ai.* attributes instead of the generic rpc.* ones. A W3C traceparent in params._meta continues the client's trace. Tool results with isError: true set error.type to tool_error, and JSON-RPC errors set rpc.status_code.

    Toolkit.handle no longer records tool arguments on the current span by default. It records gen_ai.tool.name and gen_ai.tool.call.id instead of tool and parameters. Provide Toolkit.RecordToolArguments as true to record arguments in gen_ai.tool.call.arguments.

    RpcServer.make accepts an onRequest option that wraps the effect running each request's middleware and handler. The callback also receives the decoded request.

  • #10342 b76a52b Thanks @lloydrichards! - Restrict the MCP Skills extension to protocol 2026-07-28. McpServer no longer declares or serves it on earlier revisions, and McpClient fails with UnsupportedError when extensions: { skills: true } is paired with an earlier protocol.

    Clients that use Skills must select McpProtocol.v2026_07_28. On earlier revisions, read known skill files with readResource.

  • #8506 1048919 Thanks @IMax153! - Build a fresh RpcServer.Protocol for every McpServer.layerStdio call. Two stdio servers merged into one layer graph previously shared a single protocol, so only the first one read its Stdio.

  • #10477 def28cc Thanks @effect-bot! - Fix McpServer.layerStdio hanging after stdin EOF with an open subscription while preserving accepted finite responses.

  • #10434 47563e9 Thanks @jpowersdev! - Reject ended transactions in MSSQL call/withoutTransforms(), Bun SQLite export and libsql reserve, and expose SqlClient.activeTransactionConnection for custom drivers.

  • #10426 9428e17 Thanks @gcanti! - Omit contentSchema annotations containing references in SchemaRepresentation.fromJsonSchemaDocument and fromJsonSchemaMultiDocument to avoid dangling references in generated schemas.

  • #10173 c45c33b Thanks @ebramanti! - Add OpenRpc.fromRpcGroup to effect/rpc.

  • #10385 077b572 Thanks @effect-bot! - Fix OtlpMetrics exporting metrics without a declared unit as dimensionless ("1"); they now export an empty unit.

    Prometheus-compatible backends no longer append _ratio to these gauge names (for example, app_build_info_ratio becomes app_build_info). Update affected queries and alerts, or set attributes: { unit: "1" } on the metric to keep the previous name.

  • #10440 ec4f272 Thanks @okxint! - OtlpTracer and OtelTracer now leave the span status Unset instead of Ok when a span succeeds, or fails without a reportable error. OtelTracer no longer overwrites a status set directly on the span in those cases.

    Queries and dashboards that filter on Ok should also match Unset.

  • #10316 251e9c9 Thanks @effect-bot! - Add a deduplicate: "untilExecuted" option to PersistedQueue offer, which lets a custom id be offered again once its element completes or fails its final attempt.

  • #10312 501c657 Thanks @asssslay! - Fix Queue.sliding(0) never waking waiting takers. Offers now hand the newest messages to waiting takers, and offers with no waiting taker are discarded instead of buffered.

  • #10354 9c530cf Thanks @Marve10s! - Fix zero-capacity bounded and dropping queues accepting offers without a new taker after Queue.offerAll, which also affected Stream.buffer({ capacity: 0 }).

  • #10362 781f31e Thanks @tim-smart! - Encode tool results with their JSON codec, so a Schema.Date or Schema.BigInt in a tool result reaches language models, MCP clients and Chat.exportJson as JSON. Provider-executed tools keep the provider's payload.

    Migration

    • A Tool.dynamic tool without a success schema must return a JSON value, even when toModelOutput replaces the model payload. Other values, such as a Date, now fail with ToolResultEncodingError. Declare a success schema, for example Schema.Date, or return a JSON value. Declare success: Schema.Void for a dynamic tool that returns undefined.
    • encodedResult of a Schema.Void tool, including Tool.make without a success schema, is now null instead of undefined.
  • #10066 8cf23ad Thanks @jpowersdev! - Fix HttpStaticServer responding 200 instead of 304 when an If-None-Match entity-tag contains a comma.

  • #10367 abe3084 Thanks @effect-bot! - Fix Effect.race, Effect.raceFirst, Effect.raceAll, Effect.raceAllFirst and Effect.timeoutOption sometimes letting a loser finish after the race settled, which could drop its result, such as an item taken from a Queue.

  • #10390 0d338bb Thanks @effect-bot! - Fix fail-mode fixed-window RateLimiter checks admitting more than limit requests per window and reporting a shortened resetAfter.

  • #10324 ecd5ef5 Thanks @fubhy! - Measure RcMap idle time, including RcMap.touch, on the clock of the context the map was made in, and run idle timers in that context instead of the last borrower's.

  • #10425 324e7a1 Thanks @effect-bot! - Requests with the same fields and values now compare equal with Equal.equals regardless of the order their fields were provided in.

  • #10407 4e86690 Thanks @effect-bot! - Speed up type-checking of flat RpcClient calls and AtomRpc query / mutation for large RPC groups. Selecting an RPC by tag no longer tests every RPC in the group, which cuts instantiations about 5x and brings peak checker memory down to roughly the level of the object-shaped client.

    The flat client's asQueue: true result now includes Cause.Done in the dequeue error type, matching the object-shaped client and the runtime, which ends the queue with Queue.end.

    Adds the Rpc.ByTag, RpcClient.RpcClient.Options, RpcClient.RpcClient.Result, AtomRpc.Query and AtomRpc.Mutation utility types.

  • #10418 a871b92 Thanks @jpowersdev! - Allow RPC handlers to fail with their server middleware's errors without repeating them in the RPC's error schema.

    • Handlers returned by RpcGroup.accessHandler can now fail with those middleware errors. Handle them, or widen error types that previously allowed only the RPC's own errors.
  • #10424 ddb8fd4 Thanks @nikhilsnayak! - Fix framed HTTP RPC batches hanging when requests rejected during dispatch produce more responses than streamBufferSize.

  • #10288 6e57c49 Thanks @fubhy! - Fix SchemaBinary encoding non-number elements of a number array as NaN or 0 instead of failing with "Expected a number" when an earlier element needs 64-bit float encoding, such as "x" in [Math.PI, "x"].

  • #10327 7b39595 Thanks @effect-bot! - Speed up SchemaBinary encoding of large Uint8Array payloads without changing the wire format.

    Uint8Array values of 16 KiB or more are now copied when the encode call returns, after the whole batch for encodeMany, so they must not change during the call. If such a value's length changes, for example because its buffer is detached, the encode fails with a SchemaError. Frames that contain these values are returned in their own buffer instead of a view into the shared output buffer.

  • #9612 422a1b3 Thanks @fubhy! - Prevent built-in prototype methods from being mistaken for user-supplied Schema.Struct field values. For example, Schema.Struct({ toString: Schema.optionalKey(Schema.String) }) now accepts {} instead of failing because the inherited toString function is not a string.

    Declared fields whose names are owned by Object.prototype now require an own property, even on inputs with custom prototypes. This lets absent optional fields stay absent and decoding defaults run; missing required fields report a missing key. Other field names retain inherited-property support.

  • #10382 5a7c02f Thanks @schickling-assistant! - Fix Schema encoding and decoding of schemas created by another copy of effect, which could return an internal symbol instead of the input or reject a missing optional key; both copies must include this fix.

  • #10348 24f2274 Thanks @effect-bot! - Fix Schema.toIso typing nested class fields as class instances for Schema.TaggedUnion and Schema.brand schemas.

  • #10355 da0c792 Thanks @effect-bot! - Fix isAnyOf on Schema.TaggedUnion and Schema.toTaggedUnion unions accepting keys that are not tags of the union, so misspelled tags and wide string arrays are now type errors.

    • Bound generic key parameters by the union's tags, for example <K extends Types.Tags<typeof Shape.Type>> or <K extends keyof typeof Shape.cases>, instead of <K extends string>.
    • For a tag list only known at runtime, use a plain boolean predicate such as (value: Shape): boolean => names.includes(value._tag). Filtering the list down to the union's tags makes isAnyOf compile, but its false branch then excludes every tag in the filtered type, even if the list is empty at runtime.
  • #10453 f43a02a Thanks @Marve10s! - Fix Schema.toEquivalence and Schema.toFormatter to derive consistently from the decoded type side, including records with transformed keys.

    Schema.toFormatter's onBefore hook now receives type-side AST nodes without encoding transformations. Update hooks that depend on encoding metadata or original AST identities to use decoded AST nodes. Use Schema.overrideToFormatter to attach formatting to a specific schema.

  • #10317 c32ad61 Thanks @effect-bot! - Stop ScopedCache entries whose lookup registers a finalizer from retaining the first requester's span and its response until the entry is removed.

  • #10451 724e62d Thanks @Marve10s! - Interrupt pending ScopedCache.get and ScopedCache.getOption calls when their lookup completes after the cache's owner scope closes.

  • #10387 13c0e77 Thanks @effect-bot! - Fix providing a generic SqlClient layer removing a driver service such as PgClient from an effect's requirements. The program type-checked as runnable but failed at runtime with Service not found.

    A driver client is no longer a subtype of SqlClient.SqlClient. Code that picks between driver layers, such as cond ? PgClient.layer(...) : MysqlClient.layer(...), may need an explicit Layer.Layer<SqlClient.SqlClient, E> annotation.

  • #10435 1f33b58 Thanks @jpowersdev! - Allow interrupting a nested withTransaction while it waits for another nested transaction to release the savepoint.

  • #10508 68e628d Thanks @effect-bot! - Skip savepoint release when an ancestor transaction ends during rollback, preserving the original failure without using the released connection.

  • #10433 a26ad63 Thanks @jpowersdev! - Fail SQL statements and nested transactions from a fiber that outlives the transaction it was forked in, instead of running them on that transaction's connection.

  • #10454 ded4ccf Thanks @Marve10s! - Fix SqlClient.withoutTransforms() keeping query name transforms when no result transform is configured, and Statement.make ignoring its compiler when called again with the same acquirer.

  • #10475 31368ab Thanks @effect-bot! - Fix RPC and MCP stdio servers exiting with code 130 instead of 0 after stdin closes and pending responses drain.

  • #10502 f665267 Thanks @gabedemesa! - Fix OpenAiStructuredOutput.toCodecOpenAI and AnthropicStructuredOutput.toCodecAnthropic rejecting optional records and tuples in the format they advertise.

  • #10326 2968901 Thanks @fubhy! - Construct a TestClock faster and with less memory in TestClock.make and TestClock.layer.

  • #10318 c0be8f6 Thanks @jpowersdev! - Fix SQL-backed PersistedQueue jobs retrying up to a second early on SQLite and MySQL, and remove an extra second of delay from delayed offers on SQLite.

  • #10463 d9101fc Thanks @effect-bot! - Fix effects hanging when Scheduler.MaxOpsBeforeYield is set to 1 or 2.

  • #10437 871e863 Thanks @kiliancs! - Fix defects from a tool handler's interrupt finalizers being discarded when a Toolkit.handle stream stops early.

  • #10393 6a5d86c Thanks @tim-smart! - Fix WorkflowEngine.layerMemory running Workflow.addFinalizer finalizers before completion when a workflow is resumed after a defect or resumed concurrently.

  • #10406 3c9a185 Thanks @effect-bot! - Add a positions option to Xml.decode and Xml.decodeString that reports the line and column of each StartElement.

Don't miss a new effect release

NewReleases is sending notifications on new releases.