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
- Expose at least one public port on the Service so a URL is generated (or attach it to a loadBalancer).
- If the service is intentionally private, access it via Cloud Map service discovery (`service.service`) or a linkable resource instead of `.url`.
- 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
- Only read `.url` on services that expose at least one public port or an ALB attachment.
- For private services use Cloud Map discovery (`svc.service`) or links instead of `.url`.
- When removing public ports, grep for `.url` consumers in the same codebase.
- Document which services in your stack are public vs internal.
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
- Cannot access `nodes.loadBalancer` when no public ports are
- You must provide the ports to expose via "loadBalancer.rules
- You must provide a container name in "loadBalancer.rules" wh
- Target group "${tgtId}" not found. Ensure the forward port m
- Cannot access the AWS Cloud Map service name for the "${this
AI-assisted analysis of anomalyco/sst@a0bd20f762 (2026-08-30).
Data as JSON: /api/errors/c96ba160ebce254b.
Report an issue: GitHub.