larksuite/cli · error

set hello_ack deadline: %w

Error message

set hello_ack deadline: %w

What it means

doHello wraps the error from conn.SetReadDeadline when it fails to install a 5-second read deadline (helloAckTimeout) on the event connection before waiting for the hello_ack frame. This means the underlying connection object refused or could not apply the deadline — the TCP connection is in a broken state and the handshake cannot proceed safely. doHello propagates it (wrapped with %w) to Run, which aborts the consume loop.

Source

Thrown at internal/event/consume/handshake.go:28

	"net"
	"os"
	"time"

	"github.com/larksuite/cli/internal/event/adapter/localbus/protocol"
)

const helloAckTimeout = 5 * time.Second // symmetric with bus-side hello read deadline

// doHello returns a bufio.Reader holding any bytes already pulled off conn so events
// buffered with the ack in one TCP segment aren't dropped.
func doHello(conn net.Conn, eventKey string, eventTypes []string, subscriptionID string) (*protocol.HelloAck, *bufio.Reader, error) {
	hello := protocol.NewHello(os.Getpid(), eventKey, eventTypes, "v1", subscriptionID)
	if err := protocol.EncodeWithDeadline(conn, hello, protocol.WriteTimeout); err != nil {
		return nil, nil, err
	}

	if err := conn.SetReadDeadline(time.Now().Add(helloAckTimeout)); err != nil {
		return nil, nil, fmt.Errorf("set hello_ack deadline: %w", err)
	}
	br := bufio.NewReader(conn)
	line, err := protocol.ReadFrame(br)
	if err != nil {
		return nil, nil, fmt.Errorf("no hello_ack received: %w", err)
	}
	// best-effort clear; if the conn is already broken, the loop's first read will surface it
	_ = conn.SetReadDeadline(time.Time{})
	msg, err := protocol.Decode(bytes.TrimRight(line, "\n"))
	if err != nil {
		return nil, nil, err
	}
	ack, ok := msg.(*protocol.HelloAck)
	if !ok {
		return nil, nil, fmt.Errorf("expected hello_ack, got %T", msg)
	}
	return ack, br, nil
}

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Reconnect: tear down the connection and retry the Run handshake loop — the connection is unusable, the deadline cannot be set.
  2. Check server-side logs around the connection time to see why the socket was reset or closed immediately after the Hello frame.
  3. If a custom/wrapped conn is passed in, ensure it embeds net.Conn (or *net.TCPConn) so SetReadDeadline works.
  4. Verify network path (proxy, firewall, keepalive) is not killing fresh connections between dial and handshake.

Example fix

// before: reusing a cached conn that may already be half-closed
conn := cachedConn
ack, _, err := doHello(conn, ...)
// after: dial fresh per handshake attempt
conn, err := dialer.DialContext(ctx, "tcp", addr)
if err != nil { return err }
ack, br, err := doHello(conn, eventKey, eventTypes, subscriptionID)
Defensive patterns

Strategy: retry

Validate before calling

if tc, ok := conn.(*net.TCPConn); !ok {
    return fmt.Errorf("conn %T does not support deadlines", conn)
}

Type guard

func supportsDeadline(conn net.Conn) bool {
    type deadlineSetter interface{ SetReadDeadline(time.Time) error }
    _, ok := conn.(deadlineSetter)
    return ok
}

Try / catch

ack, br, err := doHello(conn, eventKey, eventTypes, subID)
if err != nil {
    var netErr net.Error
    if errors.As(err, &netErr) {
        conn.Close()
        return retryWithBackoff(ctx) // redial; conn is unusable
    }
    return err
}

Prevention

When it happens

Trigger: conn.SetReadDeadline(time.Now().Add(helloAckTimeout)) returns a non-nil error inside doHello (internal/event/consume/handshake.go:27-29), typically because the net.Conn is already closed, the peer reset the connection immediately after the Hello frame was written, or a non-deadline-capable conn was injected (e.g. in tests or a wrapped transport).

Common situations: The event bus server closed/reset the TCP connection right after accepting the Hello (crash, restart, idle timeout, firewall drop); the client used a stale pooled connection; a custom dialer or test fake returns a conn whose SetReadDeadline is unsupported or already errored.

Related errors


AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/e678b40af29a3241. Report an issue: GitHub.