anomalyco/sst · error · VisibleError

Cannot access `nodes.loadBalancer` when no public ports are

Error message

Cannot access `nodes.loadBalancer` when no public ports are exposed.

What it means

This is the deploy-time counterpart to the dev-mode guard: `nodes.loadBalancer` is only valid when the Service was created with public ports exposed. If no load balancer exists (`self.loadBalancer` is undefined), SST throws this `VisibleError` to explain that the property is unavailable rather than returning undefined.

Source

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

       * The Amazon ECS Task Definition.
       */
      get taskDefinition() {
        if ($dev)
          throw new VisibleError(
            "Cannot access `nodes.taskDefinition` in dev mode.",
          );
        return self.taskDefinition!;
      },
      /**
       * The Amazon Elastic Load Balancer.
       */
      get loadBalancer() {
        if ($dev)
          throw new VisibleError(
            "Cannot access `nodes.loadBalancer` in dev mode.",
          );
        if (!self.loadBalancer)
          throw new VisibleError(
            "Cannot access `nodes.loadBalancer` when no public ports are exposed.",
          );
        return self.loadBalancer;
      },
    };
  }

  /** @internal */
  public getSSTLink() {
    return {
      properties: { url: $dev ? this.devUrl : this._url },
    };
  }
}

const __pulumiType = "sst:aws:Service";
// @ts-expect-error
Service.__pulumiType = __pulumiType;

View on GitHub (pinned to a0bd20f762)

Solutions

  1. Expose at least one public port via the `public` ports config so a load balancer is created.
  2. Guard the access: check `if (!$dev)` and only read `nodes.loadBalancer` for services with public ports.
  3. If the service must be internal, reference your own separately-created `sst.aws.LoadBalancer` component instead.

Example fix

// before
const lb = svc.nodes.loadBalancer;

// after
const svc = new sst.aws.Service("Api", {
  containers: [{ name: "app", image: "...", public: { ports: [{ http: 80 }] } }]
});
const lb = svc.nodes.loadBalancer; // now valid
Defensive patterns

Strategy: validation

Validate before calling

// ensure the Service config exposes public ports before reading nodes.loadBalancer
const hasPublicPorts = (args: sst.aws.ServiceArgs) =>
  args.containers?.some((c) => c.public?.ports && c.public.ports.length > 0) ?? false;
if (!hasPublicPorts(serviceArgs)) throw new Error("Add public ports to use nodes.loadBalancer");

Type guard

function exposesPublicPorts(args: sst.aws.ServiceArgs): boolean {
  return Array.isArray(args.containers) &&
    args.containers.some((c) => (c.public?.ports?.length ?? 0) > 0);
}

Try / catch

try {
  return svc.nodes.loadBalancer;
} catch (e) {
  if (e instanceof VisibleError && e.message.includes("no public ports")) return null;
  throw e;
}

Prevention

When it happens

Trigger: On `sst deploy`, reading `nodes.loadBalancer` from a Service that has no `public` ports in its container/port config, or that uses an external ALB attachment without a managed LB.

Common situations: Teams switching a service from public to internal ports (or removing the `public` config) and forgetting downstream code still reads `nodes.loadBalancer`; internal-only services where no ALB is created.

Related errors


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