anomalyco/sst · error · VisibleError

Protocols must be either all http/https, or all tcp/udp/tcp_

Error message

Protocols must be either all http/https, or all tcp/udp/tcp_udp/tls.

What it means

A Service's public ports must be uniformly either application-layer (http/https, backed by an ALB) or network-layer (tcp/udp/tcp_udp/tls, backed by an NLB) — the underlying load balancer type can't mix both. normalizePublic counts http/https ports and throws if only some of the ports are app-protocol.

Source

Thrown at platform/src/components/aws/service-v1.ts:267

        const ports = pub.ports.map((v) => {
          const listenParts = v.listen.split("/");
          const forwardParts = v.forward ? v.forward.split("/") : listenParts;
          return {
            listenPort: parseInt(listenParts[0]),
            listenProtocol: listenParts[1],
            forwardPort: parseInt(forwardParts[0]),
            forwardProtocol: forwardParts[1],
          };
        });

        // validate protocols are consistent
        const appProtocols = ports.filter(
          (port) =>
            ["http", "https"].includes(port.listenProtocol) &&
            ["http", "https"].includes(port.forwardProtocol),
        );
        if (appProtocols.length > 0 && appProtocols.length < ports.length)
          throw new VisibleError(
            `Protocols must be either all http/https, or all tcp/udp/tcp_udp/tls.`,
          );

        // validate certificate exists for https/tls protocol
        ports.forEach((port) => {
          if (["https", "tls"].includes(port.listenProtocol) && !pub.domain) {
            throw new VisibleError(
              `You must provide a custom domain for ${port.listenProtocol.toUpperCase()} protocol.`,
            );
          }
        });

        return ports;
      });

      const domain = output(args.public).apply((pub) => {
        if (!pub.domain) return undefined;

View on GitHub (pinned to a0bd20f762)

Solutions

  1. Make all `public.ports` entries use http/https protocols
  2. Or make all entries use tcp/udp/tcp_udp/tls protocols
  3. Split into two separate Service components if both protocol families are genuinely needed

Example fix

// before
public: { ports: [{ listen: "80/http" }, { listen: "22/tcp" }] }
// after
public: { ports: [{ listen: "80/http" }, { listen: "443/https" }] }
Defensive patterns

Strategy: validation

Validate before calling

const app = ["http","https"]; const net = ["tcp","udp","tcp_udp","tls"];
const protos = args.public?.ports?.map(p => p.listen.split("/")[1]) ?? [];
const allApp = protos.every(p => app.includes(p));
const allNet = protos.every(p => net.includes(p));
if (protos.length && !allApp && !allNet) throw new Error("mixing app and network protocols in public.ports");

Prevention

When it happens

Trigger: Mixing e.g. `[{ listen: "80/http" }, { listen: "22/tcp" }]` in `public.ports`; adding one TCP port to an otherwise HTTP service.

Common situations: Exposing both an HTTP API and a raw TCP port (e.g. SSH, game server) on one service; incremental edits adding a port with a different protocol.

Understand the failure class

Related errors


AI-assisted analysis of anomalyco/sst@a0bd20f762 (2026-08-30). Data as JSON: /api/errors/6a994da6e06fdc4f. Report an issue: GitHub.