Bash sandbox blocks Go binaries' DNS resolution for hosts already on `allowedHosts`

Resolved 💬 2 comments Opened May 23, 2026 by f-tachyonlabs Closed Jun 23, 2026

Summary

The Claude Code Bash sandbox returns no such host to Go's net.LookupHost for hostnames that are explicitly on its allowedHosts list. Both Go's pure-Go resolver and the cgo (libc) resolver fail identically. The same hostnames resolve cleanly via nslookup, dig, and curl from the same Bash sandbox in the same shell.

Practical impact: third-party Go-based CLIs cannot run inside the sandbox even when the hosts they talk to are allowlisted. I hit this with Google's Antigravity CLI (agy), but it applies to any Go binary that hits a network. The CLI exits 0 with empty stdout, so the failure is invisible unless you happen to inspect its log files.

Reproduction

Inside a Claude Code Bash session on macOS (darwin/arm64, Claude Code v2.x):

# Hosts that ARE on the sandbox allowlist (confirmed via curl/nslookup):
#   cloudcode-pa.googleapis.com
#   play.googleapis.com
#   storage.googleapis.com

# curl reaches them (404 = reachable, just no path):
$ curl -s -o /dev/null -w "%{http_code}" https://cloudcode-pa.googleapis.com/
404
$ curl -s -o /dev/null -w "%{http_code}" https://play.googleapis.com/
404

# nslookup resolves them:
$ nslookup cloudcode-pa.googleapis.com | grep Address | tail -1
Address: 172.217.29.42

# But a minimal Go program in the same shell fails for ALL of them,
# including the allowlisted ones, with both the pure-Go and cgo resolvers:
$ cat > "$TMPDIR/dnstest.go" <<'GO'
package main
import ("fmt"; "net"; "os")
func main() {
  for _, h := range []string{
    "cloudcode-pa.googleapis.com",          // ALLOWLISTED
    "play.googleapis.com",                  // ALLOWLISTED
    "daily-cloudcode-pa.googleapis.com",    // not on allowlist
  } {
    if ips, err := net.LookupHost(h); err != nil {
      fmt.Fprintf(os.Stderr, "FAIL %s: %v\n", h, err)
    } else {
      fmt.Printf("OK %s: %v\n", h, ips)
    }
  }
}
GO
$ go run "$TMPDIR/dnstest.go"
FAIL cloudcode-pa.googleapis.com: lookup cloudcode-pa.googleapis.com: no such host
FAIL play.googleapis.com: lookup play.googleapis.com: no such host
FAIL daily-cloudcode-pa.googleapis.com: lookup daily-cloudcode-pa.googleapis.com: no such host

$ GODEBUG=netdns=cgo go run "$TMPDIR/dnstest.go"
go package net: GODEBUG setting forcing use of the cgo resolver
FAIL cloudcode-pa.googleapis.com: lookup cloudcode-pa.googleapis.com: no such host
FAIL play.googleapis.com: lookup play.googleapis.com: no such host
FAIL daily-cloudcode-pa.googleapis.com: lookup daily-cloudcode-pa.googleapis.com: no such host

Both the pure-Go path (Go reads /etc/resolv.conf and makes UDP queries directly) and the cgo path (Go calls libc's getaddrinfo) fail. The macOS resolver does not honor HOSTALIASES for FQDNs, so that workaround doesn't apply either.

Diagnosis

curl and nslookup are exempt from whatever DNS-blocking the sandbox applies to general-purpose binaries. Go binaries are not. The pattern is consistent with a sandbox profile that admits dns.proc.dyn.allow for specific binary paths (/usr/bin/curl, /usr/bin/nslookup, /usr/bin/dig) but defaults to deny for everything else, regardless of which host the call would resolve to.

Impact

Any Go-based CLI that needs network access fails inside the Claude Code Bash sandbox even when its endpoints are explicitly on the allowedHosts list. Concrete blocked tools I've encountered:

  • agy (Google Antigravity CLI, used by /oracle in abitai-stack for the Gemini voice in council mode)
  • Likely many others: gcloud components, hashicorp/* CLIs, anything written in Go

The failure mode is silent: agy exits 0 with empty stdout and a log file full of dial tcp: lookup cloudcode-pa.googleapis.com: no such host. The caller has no way to distinguish "the model declined to respond" from "the sandbox blocked the network."

I worked around this on the abitai-stack side by adding inline detection that inspects agy's log file after each call and emits a stderr signal (ANTIGRAVITY_SANDBOX_DNS_BLOCKED) when it sees the pattern — see tachyonlabs-ai/abitai-stack#104. But the workaround is per-binary and doesn't help other Go tools.

Proposed fix

Permit DNS resolution for allowedHosts regardless of which binary makes the call. Either:

  1. Filter at the DNS layer, not at the binary path. If a hostname is on allowedHosts, let any process resolve it.
  2. Add an explicit allowlist of resolver-capable binaries that includes Go's runtime DNS path. (Less clean — Go binaries don't share a single executable path.)

Option 1 is the right fix. The current behavior makes the allowedHosts setting half-effective: TCP connections to the listed hosts succeed, but DNS resolution from anything other than libc + a small allowlist of binaries fails, so most non-trivial network code can't reach the hosts the user is supposedly allowed to reach.

Workaround for now

Users hitting this in their own Bash sessions can:

  1. Run the affected Go binary from an unsandboxed terminal (system Terminal/iTerm, not Claude Code).
  2. Use a tool with a non-Go DNS path (e.g. curl) where one exists.
  3. For /oracle users specifically: skip Antigravity with /oracle --via codex or /oracle --via qwen (PR #104 detects the block automatically and council mode degrades to 2-of-3).

Environment

  • Platform: darwin 25.5.0 (arm64)
  • Shell: zsh 5.9
  • Go: 1.26.3 darwin/arm64
  • Affected binary observed in: agy 1.0.1 (Google Antigravity CLI, installed to ~/.local/bin/agy)
  • Sandbox config visible to me via the system prompt: "allowedHosts":[..., "cloudcode-pa.googleapis.com", "oauth2.googleapis.com", "play.googleapis.com", "storage.googleapis.com", ...]

Happy to test a fix or provide more reproductions. The minimal Go program above reproduces it in ~10 seconds.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗