Skip to content

@effect/platform-node: multipart request without boundary becomes a defect (Unhandled error InvalidBoundary) instead of MultipartError #9253

Closed

Description

@cucumber-sp

What version of Effect is running?

[email protected], @effect/[email protected] (the relevant code is unchanged in @effect/[email protected]). Node.js 26.11.1.

What steps can reproduce the bug?

import { Readable } from "node:stream"
import { Cause, Effect, Exit, Stream } from "effect"
import * as NodeMultipart from "@effect/platform-node/NodeMultipart"

const body = Readable.from([
  Buffer.from('--x\r\nContent-Disposition: form-data; name="a"\r\n\r\n1\r\n--x--\r\n')
])

const exit = await Effect.runPromiseExit(
  Stream.runCollect(
    // multipart content type without a boundary parameter
    NodeMultipart.stream(body, { "content-type": "multipart/form-data" })
  )
)
console.log(Cause.hasDies(exit.cause), Cause.pretty(exit.cause).split("\n")[0])
// true Error: Unhandled error. ({ _tag: 'InvalidBoundary' })

The same happens through HttpServerRequest.multipartStream / .multipart on NodeHttpServer: a request with Content-Type: multipart/form-data and no boundary turns into a defect, so the server answers 500 instead of letting the handler map a MultipartError.

What is the expected behavior?

The stream fails with a typed Multipart.MultipartError (reason Parse or similar), like other malformed bodies do, so handlers can respond 400.

What do you see instead?

A defect: Error: Unhandled error. ({ _tag: 'InvalidBoundary' }).

Additional information

It looks like MultipartStream's constructor (packages/platform/node/src/NodeMultipartParser.ts) builds the parser, and the parser reports the invalid boundary synchronously through onError, which calls this.emit("error", error) before anyone has attached an "error" listener — so Node's EventEmitter throws inside NodeStream.fromReadable's evaluate (NodeMultipart.stream), and the throw becomes a defect instead of going through onError: convertError.

Workaround we use: check the boundary up front with MultipartParser.make(...) from effect (it parses the same content-type header) and return 400 before calling multipartStream.

Activity

  1. self-assigned this
    on Oct 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions