Our experience, from having gone that route internally and externally (e.g. with JSON-RPC), is that it ends up not paying off to have that separation.
From the client side, developers shouldn't need to worry about what piece of your stack was at fault. They want to know if it's your fault or theirs/their user's. And if it's their fault, how to prevent it or fix it. Both HTTP and gRPC are flexible enough to encode that information.
From the server side, it complicates monitoring in practice if errors are propagated encoded in the response body of a "successful" RPC. With reverse proxies and other things in place, the complication increases, and many people get unhappy.
W.r.t. migrating clients to other transports, the client library should hide that (we haven't done that always right in the past, but we do now).
From the client side, developers shouldn't need to worry about what piece of your stack was at fault. They want to know if it's your fault or theirs/their user's. And if it's their fault, how to prevent it or fix it. Both HTTP and gRPC are flexible enough to encode that information.
From the server side, it complicates monitoring in practice if errors are propagated encoded in the response body of a "successful" RPC. With reverse proxies and other things in place, the complication increases, and many people get unhappy.
W.r.t. migrating clients to other transports, the client library should hide that (we haven't done that always right in the past, but we do now).