anomalyco/sst · error · VisibleError

You must provide a custom domain for ${port.listenProtocol.t

Error message

You must provide a custom domain for ${port.listenProtocol.toUpperCase()} protocol.

What it means

Ports listening on https or tls require TLS termination at a custom domain. If a port's listen protocol is https/tls but `public.domain` is not set, normalizePublic throws, since the load balancer has no certificate to serve HTTPS/TLS without a domain.

Source

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

            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;

        // normalize domain
        const domain =
          typeof pub.domain === "string" ? { name: pub.domain } : pub.domain;
        return {
          name: domain.name,
          dns: domain.dns === false ? undefined : domain.dns ?? awsDns(),
          cert: domain.cert,

View on GitHub (pinned to a0bd20f762)

Solutions

  1. Add `domain` to the `public` block, e.g. `public: { domain: "api.example.com", ports: [...] }`
  2. Or change the listen protocol to `http` if TLS isn't needed at the LB
  3. Ensure a certificate for the domain is available in the region (Route53/ACM)

Example fix

// before
public: { ports: [{ listen: "443/https", forward: "8080/http" }] }
// after
public: { domain: "api.example.com", ports: [{ listen: "443/https", forward: "8080/http" }] }
Defensive patterns

Strategy: validation

Validate before calling

const needsDomain = args.public?.ports?.some(p => /^(443\/)?(https|tls)/.test(p.listen));
if (needsDomain && !args.public?.domain) throw new Error("https/tls ports require public.domain");

Type guard

function needsDomain(pub?: { ports?: { listen: string }[]; domain?: string }): boolean {
  return !!pub?.ports?.some(p => ["https","tls"].includes(p.listen.split("/")[1])) && !pub.domain;
}

Prevention

When it happens

Trigger: `public.ports` contains `"443/https"` or `"443/tls"` while `public.domain` is undefined; forgetting `domain` when switching a port from http to https.

Common situations: Adding TLS to an existing service; assuming a default AWS-provided domain supports https; missing certificate/hostname config.

Related errors


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