chenhg5/cc-connect · error

invalid permission decision %q

Error message

invalid permission decision %q

What it means

After decoding a BridgeResponse, Relay validates that the decision field is exactly "allow" or "deny". This error is thrown when the bridge returned a decision value outside that enum (including an empty string when a malformed/truncated JSON object decoded with zeroed fields). It protects the hook protocol so only unambiguous decisions are forwarded to Antigravity.

Source

Thrown at agent/antigravityhook/protocol.go:68

		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)
	}

	if err := json.NewEncoder(out).Encode(response); err != nil {
		return fmt.Errorf("write hook response: %w", err)
	}
	return nil
}

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Upgrade the antigravityhook binary and cc-connect to matching versions so the bridge response schema is aligned
  2. Inspect what the bridge actually returned (log the raw response before validation) to identify the unexpected decision value
  3. Check whether the bridge hit an internal error and replied with a non-decision payload; fix that error first
  4. Ensure no unrelated service is bound to the CC_CONNECT_AGY_PERMISSION_ADDR port echoing foreign JSON
Defensive patterns

Strategy: validation

Validate before calling

var resp BridgeResponse
if err := json.Unmarshal(raw, &resp); err != nil { return err }
if resp.Decision != "allow" && resp.Decision != "deny" { return fmt.Errorf("bad decision %q", resp.Decision) }

Type guard

func validDecision(d string) bool { return d == "allow" || d == "deny" }

Try / catch

if err := Relay(in, out, addr, token); err != nil {
    if strings.Contains(err.Error(), "invalid permission decision") {
        log.Error("bridge returned malformed decision — check version compatibility", "err", err)
    }
    return err
}

Prevention

When it happens

Trigger: The bridge peer on CC_CONNECT_AGY_PERMISSION_ADDR sends a JSON object whose "decision" is not "allow" or "deny" — e.g. an error payload like {"error":"..."}, an empty JSON object {} (decision decodes to ""), or a bridge implementation using different decision vocabulary.

Common situations: Version mismatch between the hook binary and the cc-connect bridge (bridge protocol changed); the bridge answered with an error object instead of a decision; a third-party service occupying the port returns its own JSON shape.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/4ac93f8f4d1c3839. Report an issue: GitHub.