chenhg5/cc-connect · error

read hook input: %w

Error message

read hook input: %w

What it means

Relay wraps the underlying io.ReadAll failure when reading the hook's stdin payload: 'read hook input: %w'. Agy pipes the hook invocation JSON into the hook's stdin; if that read fails at the OS level the error is wrapped and returned, causing the hook to fail closed (deny).

Source

Thrown at agent/antigravityhook/protocol.go:39

type BridgeRequest struct {
	Token     string          `json:"token"`
	HookInput json.RawMessage `json:"hook_input"`
}

type BridgeResponse struct {
	Decision string `json:"decision"`
	Reason   string `json:"reason,omitempty"`
}

// Relay forwards one Agy hook invocation to the owning cc-connect session.
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 {

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Verify the hook is invoked by agy normally (stdin piped) rather than run interactively
  2. Retry the triggering action in agy — usually transient
  3. Inspect the wrapped %w error for the exact OS cause (e.g. 'file already closed')
  4. If testing, pass a real JSON payload via stdin: `echo '{}' | hook`
Defensive patterns

Strategy: retry

Try / catch

if err := Relay(in, out, addr, tok); err != nil && strings.HasPrefix(err.Error(), "read hook input:") {
    log.Printf("transient stdin read failure, retrying: %v", err)
}

Prevention

When it happens

Trigger: io.ReadAll(io.LimitReader(in, maxHookInput+1)) returns err != nil — stdin closed prematurely, broken pipe from the parent agy process, or I/O error on the underlying fd.

Common situations: agy killed mid-hook invocation; hook stdin mis-wired when run manually for debugging; container/pty environments that close stdin early.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


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