chenhg5/cc-connect · error
connect permission bridge
Error message
connect permission bridge: %w
What it means
Relay wraps net.DialTimeout failures as 'connect permission bridge: %w'. The hook dials the cc-connect session's TCP listener (5s timeout) to ask the human for a permission decision; if the connection cannot be established the hook fails closed (deny). Per the code comment, dial failures are intentionally fast because the listener is expected to already be up.
Solutions
- Confirm cc-connect (and the antigravity session) is still running while agy executes
- Verify CC_CONNECT_AGY_PERMISSION_ADDR matches the listener the session actually bound
- Test connectivity: `nc -vz <host> <port>` from the same environment the hook runs in
- Check firewall/container networking allows the hook to reach the bridge address
- If it races session shutdown, this fail-closed behavior is expected — treat it as a deny
Example fix
// before $ CC_CONNECT_AGY_PERMISSION_ADDR=127.0.0.1:0 agy ... // after $ CC_CONNECT_AGY_PERMISSION_ADDR=127.0.0.1:47391 agy ... # port from the live listener
Defensive patterns
Strategy: retry
Validate before calling
conn, err := net.DialTimeout("tcp", addr, 2*time.Second)
if err != nil { log.Fatalf("bridge %s unreachable before launching agy: %v", addr, err) }
conn.Close() Try / catch
if err := Relay(...); err != nil {
var ne net.Error
if errors.As(err, &ne) || strings.HasPrefix(err.Error(), "connect permission bridge:") {
// treat as deny; optionally retry with backoff while session is alive
}
} Prevention
- Verify the bridge address env matches the live listener port
- Ensure the session outlives the agy hook (no concurrent Stop)
- Check firewall/container networking for the bridge port
- `nc -vz host port` from the hook environment as a preflight
When it happens
Trigger: DialTimeout to CC_CONNECT_AGY_PERMISSION_ADDR fails: cc-connect listener not running, session already exited, wrong address/port in env, firewall blocking loopback/tcp, or listener bound after the hook ran.
Common situations: Stale env vars pointing at a dead session's port; agy hook racing session shutdown; cc-connect crashed while agy kept running; Docker/container networking isolating the hook from the host port.
Understand the failure class
Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.
Related errors
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/7faa71a5b43827b1.
Report an issue: GitHub.
Appendix: source
Thrown at agent/antigravityhook/protocol.go:50
func Relay(in io.Reader, out io.Writer, address, token string) error {
if strings.TrimSpace(address) == "" || strings.TrimSpace(token) == "" {
return fmt.Errorf("permission bridge environment is missing")
}
input, err := io.ReadAll(io.LimitReader(in, maxHookInput+1))
if err != nil {
return fmt.Errorf("read hook input: %w", err)
}
if len(input) > maxHookInput {
return fmt.Errorf("hook input exceeds %d bytes", maxHookInput)
}
if !json.Valid(input) {
return fmt.Errorf("hook input is not valid JSON")
}
conn, err := net.DialTimeout("tcp", address, bridgeDialTimeout)
if err != nil {
return fmt.Errorf("connect permission bridge: %w", err)
}
defer func() { _ = conn.Close() }()
// The listener is started before agy runs this hook, so dial failures should
// fail closed quickly. After connect, wait much longer for a human response.
_ = conn.SetDeadline(time.Now().Add(bridgeResponseTimeout))
if err := json.NewEncoder(conn).Encode(BridgeRequest{Token: token, HookInput: input}); err != nil {
return fmt.Errorf("send permission request: %w", err)
}
var response BridgeResponse
if err := json.NewDecoder(io.LimitReader(conn, 64<<10)).Decode(&response); err != nil {
return fmt.Errorf("read permission response: %w", err)
}
switch response.Decision {
case "allow", "deny":
default:
return fmt.Errorf("invalid permission decision %q", response.Decision)View on GitHub (pinned to 4000b2338a)