So I debugged through and it turns out simply throwing a synchronous exception does exactly what you'd want.

1) It's caught by the catch in ChannelHandlerInvokerUtil.invokeChannelReadNow which fires the exception down the handler chain.

2) Http2ConnectionHandler.exceptionCaught sees the connection error, sends a GoAway frame, and closes the channel.

So I think anything other than synchronous throw is redundant here, assuming the handler chain is already setup.