{"record":{"id":"34c41280e1b565db","repo":"nektos/act","slug":"unable-to-determine-outbound-ip-address","errorCode":null,"errorMessage":"unable to determine outbound IP address","messagePattern":"unable to determine outbound IP address","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/artifactcache/handler.go","lineNumber":85,"sourceCode":"\t\treturn nil, err\n\t}\n\n\th.dir = dir\n\n\tstorage, err := NewStorage(filepath.Join(dir, \"cache\"))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\th.storage = storage\n\n\tif customExternalURL != \"\" {\n\t\th.customExternalURL = customExternalURL\n\t}\n\n\tif outboundIP != \"\" {\n\t\th.outboundIP = outboundIP\n\t} else if ip := common.GetOutboundIP(); ip == nil {\n\t\treturn nil, fmt.Errorf(\"unable to determine outbound IP address\")\n\t} else {\n\t\th.outboundIP = ip.String()\n\t}\n\n\ttokenBytes := make([]byte, 16)\n\tif _, err := rand.Read(tokenBytes); err != nil {\n\t\treturn nil, fmt.Errorf(\"generate auth token: %w\", err)\n\t}\n\th.token = hex.EncodeToString(tokenBytes)\n\n\trouter := httprouter.New()\n\tbase := \"/\" + h.token + apiPath\n\trouter.GET(base+\"/cache\", h.middleware(h.find))\n\trouter.POST(base+\"/caches\", h.middleware(h.reserve))\n\trouter.PATCH(base+\"/caches/:id\", h.middleware(h.upload))\n\trouter.POST(base+\"/caches/:id\", h.middleware(h.commit))\n\trouter.GET(base+\"/artifacts/:id\", h.middleware(h.get))\n\trouter.POST(base+\"/clean\", h.middleware(h.clean))","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/nektos/act/blob/4f411281417e88660bea1c1a1749aa71ae0bd60f/pkg/artifactcache/handler.go#L67-L103","documentation":"When the artifact cache server starts, it needs an outbound IP to advertise in artifact/cache download URLs served to containers. If no explicit outbound IP was configured and common.GetOutboundIP() (which dials an external address, typically 8.8.8.8, to learn the default-route source address) returns nil — no route to the internet — the handler constructor fails with 'unable to determine outbound IP address'.","triggerScenarios":"Running act (which starts the artifact/cache server automatically for every job) on a host with no external network route: air-gapped machines, firewalls blocking UDP/TCP to the probe address, containers or VMs without a default gateway.","commonSituations":"Corporate/air-gapped environments where the dial-out probe silently fails; CI runners in isolated VLANs; act running inside a container itself without outbound access; misconfigured DNS/routing after VPN changes.","solutions":["Pass an explicit outbound IP so no probe is needed: 'act --artifact-server-ip <host-ip>' (or set the corresponding Input field when using act as a library).","Restore basic outbound connectivity: default route present and the probe address reachable (check 'ip route' / routing table).","In locked-down networks, allow the host to dial the external probe address once, or always pin the IP as above.","As a workaround for fully offline runs, disable artifact/cache features if your workflows do not use them."],"exampleFix":"# before\nact -j build\n# -> unable to determine outbound IP address (no internet route)\n\n# after\nact --artifact-server-ip 192.168.1.20 -j build","handlingStrategy":"validation","validationCode":"package main\n\nimport (\n\t\"fmt\"\n\t\"net\"\n)\n\nfunc outboundIPOrExplicit(explicit string) (string, error) {\n\tif explicit != \"\" {\n\t\treturn explicit, nil\n\t}\n\tconn, err := net.Dial(\"udp\", \"8.8.8.8:80\")\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"no outbound route; pass --artifact-server-ip explicitly\")\n\t}\n\tdefer conn.Close()\n\treturn conn.LocalAddr().(*net.UDPAddr).IP.String(), nil\n}","typeGuard":null,"tryCatchPattern":"handler, err := artifactcache.NewHandler(dir, externalURL, outboundIP, \"\")\nif err != nil {\n    if strings.Contains(err.Error(), \"unable to determine outbound IP address\") {\n        // offline host: bind to an explicit local address and retry\n        handler, err = artifactcache.NewHandler(dir, externalURL, mustDefaultOutboundIP(), \"\")\n    }\n    if err != nil {\n        return err\n    }\n}","preventionTips":["Pin the advertised IP with --artifact-server-ip on hosts without internet routes.","Verify a default route exists ('ip route get 8.8.8.8') before running act on new networks.","Document the artifact-server IP in runner setup docs for air-gapped environments."],"tags":["network","artifact-cache","offline","environment"],"backgroundTag":null,"analyzedSha":"4f411281417e88660bea1c1a1749aa71ae0bd60f","analyzedAt":"2026-08-15T09:19:46.307Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}