{"record":{"id":"172fca0652946f18","repo":"larksuite/cli","slug":"no-hello-ack-received-w","errorCode":null,"errorMessage":"no hello_ack received: %w","messagePattern":"no hello_ack received: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/event/consume/handshake.go","lineNumber":33,"sourceCode":")\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}\n","sourceCodeStart":15,"sourceCodeEnd":47,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/event/consume/handshake.go#L15-L47","documentation":"After installing the 5s read deadline, doHello reads the hello_ack frame with protocol.ReadFrame; this error wraps any read failure — most commonly the 5-second helloAckTimeout expiring (i/o timeout) or the connection closing before any ack arrived. The library throws it because a Hello without a HelloAck means the server did not accept the subscription handshake, so the event stream cannot start.","triggerScenarios":"protocol.ReadFrame(br) returns an error after a Hello frame was sent on the conn: server never replied within helloAckTimeout (5s), server closed the TCP connection, or sent a malformed/oversized frame that the framer rejects.","commonSituations":"Event bus is down or overloaded and not answering handshakes; wrong endpoint/port (connected to something that is not the bus); server rejected the connection silently after Hello; network partition or TLS/proxy stripping the stream; server version speaks a different frame protocol.","solutions":["Retry with backoff: Run's reconnect loop should redial — a timeout is often transient (server restart or load).","Verify the dial address points at the correct event-bus host/port that speaks this frame protocol.","Check server health/logs at the timestamp to see whether the Hello arrived and why no ack was emitted.","Inspect protocol compatibility: client and server must agree on frame encoding and hello_ack shape (protocol version 'v1').","If timeouts are frequent, investigate network latency/keepalive between client and bus."],"exampleFix":"// before: single attempt, hard fail\nack, br, err := doHello(conn, eventKey, eventTypes, subID)\nif err != nil { return err }\n// after: let Run's reconnect loop retry transient handshake failures\nif err := backoff.Retry(ctx, func() error {\n    _, err := runOnce(ctx)\n    return err\n}); err != nil { return err }","handlingStrategy":"retry","validationCode":"conn, err := net.DialTimeout(\"tcp\", addr, 3*time.Second)\nif err != nil {\n    return fmt.Errorf(\"event bus unreachable at %s: %w\", addr, err)\n}","typeGuard":"func isHelloAckTimeout(err error) bool {\n    var ne net.Error\n    return errors.As(err, &ne) && ne.Timeout()\n}","tryCatchPattern":"if _, _, err := doHello(conn, key, types, subID); err != nil {\n    if isHelloAckTimeout(err) {\n        conn.Close()\n        return backoffRetry(ctx) // transient: server slow or restarting\n    }\n    return err // non-timeout read failure: surface to caller\n}","preventionTips":["Deploy Run with a reconnect/backoff loop so 5s ack timeouts self-heal.","Health-check the bus endpoint before dialing in ops scripts.","Keep client and server frame-protocol versions in lockstep.","Alert on handshake failure rate to catch bus outages early."],"tags":["network","timeout","handshake","tcp"],"backgroundTag":"handshake-ack-timeout","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"}