What version of Effect is running?
3.19.18
What steps can reproduce the bug?
It seems like middleware is being ignored entirely under certain conditions.
This is as minimally I could reproduce the issue:
import { createServer } from "node:http";
import {
HttpApi,
HttpApiBuilder,
HttpApiEndpoint,
HttpApiError,
HttpApiGroup,
HttpApiMiddleware,
HttpApiSecurity,
HttpLayerRouter,
} from "@effect/platform";
import { NodeHttpServer, NodeRuntime } from "@effect/platform-node";
import { Context, Effect, Layer, Schema } from "effect";
export class Session extends Context.Tag("app/Session")<
Session,
{
id: string;
}
>() {}
class InternalAuthorization extends HttpApiMiddleware.Tag<InternalAuthorization>()(
"InternalAuthorization",
{
failure: HttpApiError.Unauthorized,
provides: Session,
security: {
apiKey: HttpApiSecurity.bearer,
},
}
) {}
const externalApi = HttpApi.make("external-api").add(
HttpApiGroup.make("Posts")
.add(
HttpApiEndpoint.get("posts", "/")
.addSuccess(Schema.String)
.addError(HttpApiError.BadRequest)
)
.prefix("/posts")
);
const internalApi = HttpApi.make("internal-api")
.add(
HttpApiGroup.make("Customers")
.add(
HttpApiEndpoint.get("customers", "/").addSuccess(
Schema.Array(Schema.String)
)
)
.prefix("/customers")
)
.add(
HttpApiGroup.make("Users")
.add(
HttpApiEndpoint.get("users", "/").addSuccess(
Schema.Array(Schema.String)
)
)
.prefix("/users")
)
.middleware(InternalAuthorization);
const InternalAuthorizationLive = Layer.succeed(InternalAuthorization, {
apiKey: () => Effect.fail(new HttpApiError.Unauthorized()),
});
const PostsLive = HttpApiBuilder.group(externalApi, "Posts", (handlers) =>
handlers.handle("posts", () => Effect.succeed("ok"))
);
const CustomersLive = HttpApiBuilder.group(
internalApi,
"Customers",
(handlers) =>
handlers.handle(
"customers",
Effect.fnUntraced(function* () {
const session = yield* Session;
return ["customer1", "customer2", session.id];
})
)
);
const UsersLive = HttpApiBuilder.group(internalApi, "Users", (handlers) =>
handlers.handle("users", () => Effect.succeed(["user1", "user2"]))
);
const ExternalRoutes = HttpLayerRouter.addHttpApi(externalApi).pipe(
Layer.provide([PostsLive])
);
const InternalRoutes = HttpLayerRouter.addHttpApi(internalApi).pipe(
Layer.provide([UsersLive, CustomersLive]),
Layer.provide(InternalAuthorizationLive)
);
const Routes = Layer.mergeAll(ExternalRoutes, InternalRoutes);
const HttpLive = HttpLayerRouter.serve(Routes).pipe(
Layer.provide(NodeHttpServer.layer(createServer, { port: 3001 }))
);
NodeRuntime.runMain(Layer.launch(HttpLive));
I would then hit the internal API:
curl -i http://localhost:3001/users
What is the expected behavior?
The users endpoint should be protected by the internal authorization middleware. Expected status code 401.
What do you see instead?
⨯ curl -i http://localhost:3001/users
HTTP/1.1 200 OK
content-type: application/json
content-length: 17
Date: Wed, 11 Mar 2026 15:41:16 GMT
Connection: keep-alive
Keep-Alive: timeout=5
["user1","user2"]
Additional information
Hitting the customers endpoint defects due to "Service not found: app/Session".
Applying the middleware to the group or route instead of the http api results in another unexpected response of 500 Internal Server Error.
@effect/platform: 0.94.5
What version of Effect is running?
3.19.18
What steps can reproduce the bug?
It seems like middleware is being ignored entirely under certain conditions.
This is as minimally I could reproduce the issue:
I would then hit the internal API:
What is the expected behavior?
The users endpoint should be protected by the internal authorization middleware. Expected status code 401.
What do you see instead?
Additional information
Hitting the customers endpoint defects due to "Service not found: app/Session".
Applying the middleware to the group or route instead of the http api results in another unexpected response of 500 Internal Server Error.
@effect/platform: 0.94.5