anomalyco/sst · error · VisibleError

Cannot access the URL because no public ports are exposed.

Error message

Cannot access the URL because no public ports are exposed.

What it means

The Service's `url` getter returns the dev URL (in dev mode) or the load balancer URL (in deploy mode). If no public ports were exposed on the Service, neither URL exists, and reading `url` throws this VisibleError to tell you there is nothing to return. It signals a caller accessing `.url` on an internally-only service.

Source

Thrown at platform/src/components/aws/service.ts:2845

              role: taskRole.arn,
            },
          });
        }
      });
    }
  }

  /**
   * The URL of the service.
   *
   * If `public.domain` is set, this is the URL with the custom domain.
   * Otherwise, it's the auto-generated load balancer URL.
   */
  public get url() {
    const errorMessage =
      "Cannot access the URL because no public ports are exposed.";
    if (this.dev) {
      if (!this.devUrl) throw new VisibleError(errorMessage);
      return this.devUrl;
    }

    if (!this._url) throw new VisibleError(errorMessage);
    return this._url;
  }

  /**
   * The name of the Cloud Map service. This is useful for service discovery.
   */
  public get service() {
    return all([this.cloudmapNamespace, this.cloudmapService]).apply(
      ([namespace, service]) => {
        if (!namespace)
          throw new VisibleError(
            `Cannot access the AWS Cloud Map service name for the "${this._name}" Service. Cloud Map is not configured for the cluster.`,
          );

View on GitHub (pinned to a0bd20f762)

Solutions

  1. Expose at least one public port on the Service so a URL is generated (or attach it to a loadBalancer).
  2. If the service is intentionally private, access it via Cloud Map service discovery (`service.service`) or a linkable resource instead of `.url`.
  3. Wrap the `.url` access in a try/catch for VisibleError, or guard on the component's configuration before reading it.

Example fix

// before
const api = new sst.aws.Service("Api", { /* no public ports */ });
export const url = api.url;
// after
const api = new sst.aws.Service("Api", {
  container: { ports: [{ listen: 8080 / http, public: true }] }
});
export const url = api.url;
Defensive patterns

Strategy: try-catch

Validate before calling

const exposesPublicPort = !!args.container?.ports?.some(p => p.public) || !!args.loadBalancer;
if (needUrl && !exposesPublicPort) {
  throw new Error("Service must expose a public port before .url can be read");
}

Type guard

function hasPublicUrl(svc: { url?: string }): boolean {
  try { return typeof svc.url === "string" && svc.url.length > 0; } catch { return false; }
}

Try / catch

let url: string;
try {
  url = svc.url;
} catch (e) {
  if (e instanceof VisibleError && e.message.includes("no public ports are exposed")) {
    url = ""; // or fall back to internal discovery
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Accessing `myService.url` when the Service was defined without any public ports (no public container ports and no loadBalancer attachment) — in both dev and deploy modes when the corresponding URL (`devUrl`/`_url`) is undefined.

Common situations: Linking a backend-only service and blindly reading `.url` in a frontend or another component; removing public ports during a refactor but leaving code that reads the URL; sharing code between services where some expose ports and some don't.

Related errors


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