{"record":{"id":"3fe6362a2f48ac8f","repo":"anomalyco/sst","slug":"target-group-tgtid-not-found-ensure-the-forw","errorCode":null,"errorMessage":"Target group \"${tgtId}\" not found. Ensure the forward port matches in Service \"${name}\".","messagePattern":"Target group \"(.+?)\" not found\\. Ensure the forward port matches in Service \"(.+?)\"\\.","errorType":"validation","errorClass":"VisibleError","httpStatus":null,"severity":"error","filePath":"platform/src/components/aws/service.ts","lineNumber":2763,"sourceCode":"        ) {\n          throw new VisibleError(\n            `At least one condition (path, query, or header) must be set for rules on an external ALB in Service \"${name}\".`,\n          );\n        }\n\n        const listenerParts = rule.listen.split(\"/\");\n        const listenerPort = parseInt(listenerParts[0]);\n        const listenerProtocol = listenerParts[1];\n\n        const forwardParts = rule.forward.split(\"/\");\n        const forwardPort = parseInt(forwardParts[0]);\n        const forwardProtocol = forwardParts[1].toUpperCase();\n        const containerNameForKey = rule.container ?? name;\n        const tgtId = targetKey(containerNameForKey, forwardProtocol, forwardPort);\n\n        const targetGroup = albTargets[tgtId];\n        if (!targetGroup) {\n          throw new VisibleError(\n            `Target group \"${tgtId}\" not found. Ensure the forward port matches in Service \"${name}\".`,\n          );\n        }\n\n        const listenerResource =\n          attachment.instance.getListener(listenerProtocol, listenerPort);\n\n        new lb.ListenerRule(\n          ...transform(\n            args.transform?.listenerRule,\n            `${name}AlbRule${listenerProtocol.toUpperCase()}${listenerPort}P${rule.priority}`,\n            {\n              listenerArn: listenerResource.arn,\n              priority: rule.priority,\n              actions: [\n                {\n                  type: \"forward\",\n                  targetGroupArn: targetGroup.arn,","sourceCodeStart":2745,"sourceCodeEnd":2781,"githubUrl":"https://github.com/anomalyco/sst/blob/a0bd20f762883e72a35caccb4896c42ce5b3f707/platform/src/components/aws/service.ts#L2745-L2781","documentation":"When a Service is attached to an existing ALB via loadBalancer rules, each rule's `forward` port/protocol must match a target group that the Service created for one of its exposed ports. The component builds a key from the rule's forward port/protocol/container and looks it up in the map of ALB target groups; if nothing matches, it throws this VisibleError so the misconfiguration is caught at synth/deploy time instead of producing an invalid listener rule.","triggerScenarios":"Setting a loadBalancer rule whose `forward` port (or protocol, or container name) does not correspond to any port exposed by the Service's containers — e.g. `forward: \"8080/http\"` while the container only exposes/exactly registers port 3000, a protocol mismatch like `forward: \"3000/https\"` when the target group was created as http, or a `container` field that names a container that has no such port.","commonSituations":"Copy-pasting an ALB rule from another service and forgetting to update the forward port; renaming a container so the default container-name key no longer matches; typo in port; assuming any forward port works without declaring the port in the container's ports list.","solutions":["Check the rule's `forward` port in the Service's loadBalancer attachment and make it exactly match a port exposed by the target container (e.g. change `forward: \"8080/http\"` to `forward: \"3000/http\"`).","Verify the protocol in `forward` matches the protocol the Service exposes for that port (http vs https).","If the rule forwards to a non-default container, set `container` on the rule to the container that actually exposes the port.","Confirm the target container declares that port in its `ports`/public ports configuration so the Service creates the target group."],"exampleFix":"// before\nloadBalancer: {\n  rules: [{ listen: \"443/https\", forward: \"8080/http\", conditions: { path: \"/api/*\" } }]\n}\n// after\nloadBalancer: {\n  rules: [{ listen: \"443/https\", forward: \"3000/http\", conditions: { path: \"/api/*\" } }]\n}","handlingStrategy":"validation","validationCode":"const svcPorts = container.ports?.map(p => `${p.port}/${p.protocol ?? \"http\"}`) ?? [];\nif (!svcPorts.includes(rule.forward)) {\n  throw new Error(`Rule forward \"${rule.forward}\" must match an exposed Service port (${svcPorts.join(\", \")})`);\n}","typeGuard":"function hasMatchingTargetGroup(albTargets: Record<string, unknown>, rule: { forward: string; container?: string }, svcName: string): boolean {\n  const [port, proto] = rule.forward.split(\"/\");\n  const key = targetKey(rule.container ?? svcName, (proto ?? \"http\").toUpperCase(), parseInt(port, 10));\n  return key in albTargets;\n}","tryCatchPattern":"try {\n  await deploy();\n} catch (e) {\n  if (e instanceof VisibleError && e.message.includes(\"Target group\") && e.message.includes(\"not found\")) {\n    console.error(\"ALB rule forward port does not match an exposed service port:\", e.message);\n  }\n  throw e;\n}","preventionTips":["Keep a single source of truth for container ports and derive both the ports config and the loadBalancer forward values from it.","Verify forward port/protocol string exactly matches a declared public port (e.g. \"3000/http\").","When forwarding to a non-primary container, always set the rule's `container` field explicitly.","Review loadBalancer rule diffs when renaming containers or changing ports."],"tags":["aws","alb","ecs","configuration","port-mismatch"],"backgroundTag":"alb-forward-port-mismatch","analyzedSha":"a0bd20f762883e72a35caccb4896c42ce5b3f707","analyzedAt":"2026-08-30T11:26:00.383Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}