{"record":{"id":"3f2266047a93b178","repo":"anomalyco/sst","slug":"you-must-provide-the-ports-to-expose-via-public-p","errorCode":null,"errorMessage":"You must provide the ports to expose via \"public.ports\".","messagePattern":"You must provide the ports to expose via \"public\\.ports\"\\.","errorType":"validation","errorClass":"VisibleError","httpStatus":null,"severity":"error","filePath":"platform/src/components/aws/service-v1.ts","lineNumber":244,"sourceCode":"        cpuUtilization: v?.cpuUtilization ?? 70,\n        memoryUtilization: v?.memoryUtilization ?? 70,\n      }));\n    }\n\n    function normalizeLogging() {\n      return output(args.logging).apply((logging) => ({\n        ...logging,\n        retention: logging?.retention ?? \"1 month\",\n      }));\n    }\n\n    function normalizePublic() {\n      if (!args.public) return;\n\n      const ports = output(args.public).apply((pub) => {\n        // validate ports\n        if (!pub.ports || pub.ports.length === 0)\n          throw new VisibleError(\n            `You must provide the ports to expose via \"public.ports\".`,\n          );\n\n        // parse protocols and ports\n        const ports = pub.ports.map((v) => {\n          const listenParts = v.listen.split(\"/\");\n          const forwardParts = v.forward ? v.forward.split(\"/\") : listenParts;\n          return {\n            listenPort: parseInt(listenParts[0]),\n            listenProtocol: listenParts[1],\n            forwardPort: parseInt(forwardParts[0]),\n            forwardProtocol: forwardParts[1],\n          };\n        });\n\n        // validate protocols are consistent\n        const appProtocols = ports.filter(\n          (port) =>","sourceCodeStart":226,"sourceCodeEnd":262,"githubUrl":"https://github.com/anomalyco/sst/blob/a0bd20f762883e72a35caccb4896c42ce5b3f707/platform/src/components/aws/service-v1.ts#L226-L262","documentation":"When `public` is provided on a Service, SST uses a load balancer and needs to know which ports to expose. If `public.ports` is missing or an empty array, normalizePublic throws because the LB would have no listeners.","triggerScenarios":"Passing `public: {}` or `public: { domain: ... }` without `ports`, or `ports: []`, to `sst.aws.Service`.","commonSituations":"Intending to make the service public but only configuring domain/TLS; copying a private-service example and adding an empty `public` block.","solutions":["Add at least one entry to `public.ports`, e.g. `ports: [{ listen: \"80/http\" }]`","If the service should be private, remove the `public` block entirely","Ensure ports entries include both listen and forward specifiers"],"exampleFix":"// before\nnew sst.aws.Service(\"Api\", { public: {} });\n// after\nnew sst.aws.Service(\"Api\", {\n  public: { ports: [{ listen: \"80/http\", forward: \"8080/http\" }] },\n});","handlingStrategy":"validation","validationCode":"if (args.public && (!args.public.ports || args.public.ports.length === 0)) {\n  throw new Error('public block requires at least one entry in \"ports\"');\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never pass an empty `public` object","Omit `public` entirely for private services","Always include listen and forward specs per port"],"tags":["ports","load-balancer","configuration"],"backgroundTag":"missing-required-config","analyzedSha":"a0bd20f762883e72a35caccb4896c42ce5b3f707","analyzedAt":"2026-08-30T11:26:00.383Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}