{"record":{"id":"e678b40af29a3241","repo":"larksuite/cli","slug":"set-hello-ack-deadline-w","errorCode":null,"errorMessage":"set hello_ack deadline: %w","messagePattern":"set hello_ack deadline: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/event/consume/handshake.go","lineNumber":28,"sourceCode":"\t\"net\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/larksuite/cli/internal/event/adapter/localbus/protocol\"\n)\n\nconst helloAckTimeout = 5 * time.Second // symmetric with bus-side hello read deadline\n\n// doHello returns a bufio.Reader holding any bytes already pulled off conn so events\n// buffered with the ack in one TCP segment aren't dropped.\nfunc doHello(conn net.Conn, eventKey string, eventTypes []string, subscriptionID string) (*protocol.HelloAck, *bufio.Reader, error) {\n\thello := protocol.NewHello(os.Getpid(), eventKey, eventTypes, \"v1\", subscriptionID)\n\tif err := protocol.EncodeWithDeadline(conn, hello, protocol.WriteTimeout); err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tif err := conn.SetReadDeadline(time.Now().Add(helloAckTimeout)); err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"set hello_ack deadline: %w\", err)\n\t}\n\tbr := bufio.NewReader(conn)\n\tline, err := protocol.ReadFrame(br)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"no hello_ack received: %w\", err)\n\t}\n\t// best-effort clear; if the conn is already broken, the loop's first read will surface it\n\t_ = conn.SetReadDeadline(time.Time{})\n\tmsg, err := protocol.Decode(bytes.TrimRight(line, \"\\n\"))\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tack, ok := msg.(*protocol.HelloAck)\n\tif !ok {\n\t\treturn nil, nil, fmt.Errorf(\"expected hello_ack, got %T\", msg)\n\t}\n\treturn ack, br, nil\n}","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/event/consume/handshake.go#L10-L46","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Reconnect: tear down the connection and retry the Run handshake loop — the connection is unusable, the deadline cannot be set.","Check server-side logs around the connection time to see why the socket was reset or closed immediately after the Hello frame.","If a custom/wrapped conn is passed in, ensure it embeds net.Conn (or *net.TCPConn) so SetReadDeadline works.","Verify network path (proxy, firewall, keepalive) is not killing fresh connections between dial and handshake."],"exampleFix":"// before: reusing a cached conn that may already be half-closed\nconn := cachedConn\nack, _, err := doHello(conn, ...)\n// after: dial fresh per handshake attempt\nconn, err := dialer.DialContext(ctx, \"tcp\", addr)\nif err != nil { return err }\nack, br, err := doHello(conn, eventKey, eventTypes, subscriptionID)","handlingStrategy":"retry","validationCode":"if tc, ok := conn.(*net.TCPConn); !ok {\n    return fmt.Errorf(\"conn %T does not support deadlines\", conn)\n}","typeGuard":"func supportsDeadline(conn net.Conn) bool {\n    type deadlineSetter interface{ SetReadDeadline(time.Time) error }\n    _, ok := conn.(deadlineSetter)\n    return ok\n}","tryCatchPattern":"ack, br, err := doHello(conn, eventKey, eventTypes, subID)\nif err != nil {\n    var netErr net.Error\n    if errors.As(err, &netErr) {\n        conn.Close()\n        return retryWithBackoff(ctx) // redial; conn is unusable\n    }\n    return err\n}","preventionTips":["Always redial a fresh connection per handshake attempt instead of reusing cached conns.","Ensure custom transports wrap net.Conn so SetReadDeadline is available.","Monitor server-side connection churn; frequent resets right after Hello indicate server instability."],"tags":["network","tcp","handshake","deadline"],"backgroundTag":"set-read-deadline-failed","analyzedSha":"7fd6ef3c07182257ce776cdc5a614e122d5bd4b3","analyzedAt":"2026-09-04T21:17:44.649Z","contentChangedAt":"2026-09-04T21:17:44.649Z","schemaVersion":2},"datasetVersion":"2026-09-12T02:17:10.037Z"}