Skip to content

Skip handling when handler is closed to prevent processing on canceled calls#820

Open
HyprUsr wants to merge 1 commit intogrpc:masterfrom
HyprUsr:skip-handling-when-handler-is-closed
Open

Skip handling when handler is closed to prevent processing on canceled calls#820
HyprUsr wants to merge 1 commit intogrpc:masterfrom
HyprUsr:skip-handling-when-handler-is-closed

Conversation

@HyprUsr
Copy link
Copy Markdown

@HyprUsr HyprUsr commented Jan 1, 2026

Summary

This PR fixes a critical server-side crash occurring when a gRPC client disconnects immediately after initiating a request. This issue typically results in an unhandled Bad state: Cannot add event after closing error, which brings down the entire server process.

Related Issues

Fixes #67 and #558 . This behavior has been reported previously but remained unresolved until now.

The Problem

When a client terminates abruptly (e.g., a process crash or network drop) immediately after sending a request, the server-side stream may transition to a closed state while the handler is still attempting to send data or events back. Attempting to interact with the closed stream without a state check throws a fatal "Bad state" exception.

As a matter of principle, a client-side disconnection should never be able to compromise the stability of the server process.

The Solution

The fix introduces a safety check on the isCanceled flag prior to any interaction with the client stream. This ensures that if the connection is already dead, the server gracefully handles the state rather than attempting to write to a closed sink.

How to Reproduce

  1. Start the gRPC server.
  2. Run the following Go script. It opens a stream, sends a single message, and then calls os.Exit(0) to simulate an abrupt termination without a clean shutdown.
  3. Observe the server logs for the Bad state: Cannot add event after closing crash.
package main

import (
	"context"
	"log"
	"os"
	"time"

	pb "your/module/path/protos" 
	"google.golang.org/grpc"
)

func main() {
	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancel()

	conn, err := grpc.DialContext(ctx, "localhost:50051",
		grpc.WithInsecure(),
		grpc.WithBlock(),
		grpc.WithDefaultCallOptions(grpc.WaitForReady(true)),
	)
	if err != nil {
		log.Fatalf("dial: %v", err)
	}

	client := pb.NewMyServiceClient(conn)
	stream, err := client.Chat(ctx) 
	if err != nil {
		log.Fatalf("open stream: %v", err)
	}

	if err := stream.Send(&pb.ChatRequest{Message: "hi"}); err != nil {
		log.Fatalf("send: %v", err)
	}

	// Terminate immediately to mimic abrupt client death.
	os.Exit(0)
}

@HyprUsr HyprUsr marked this pull request as ready for review January 1, 2026 11:25
@HyprUsr HyprUsr force-pushed the skip-handling-when-handler-is-closed branch from 70b8dac to 64b3c0c Compare January 1, 2026 19:22
@grpc grpc deleted a comment from tsavo-at-pieces Feb 24, 2026
@grpc grpc deleted a comment from tsavo-at-pieces Feb 24, 2026
@grpc grpc deleted a comment from tsavo-at-pieces Feb 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

"Cannot add event after closing" server error when client abruptly goes away

1 participant