encoding/gob: better support for error
mbonell opened this issue · comments
Errors created by the errors package are not registered with encoding/gob which is necessary in order to send them as results of remote procedure calls.
By now, returning an error from the standard library through an RPC method will cause that the receiver get an EOF error and the transmitter a gob: type not registered for interface: errors.errorString
My proposal is add an init() function in the errors package to register the struct errorString so then, developers will avoid create its own errors implementation when using RPCs.
Can you show some code demonstrating where this is needed? Thanks.
Here: https://golang.org/src/errors/errors.go
We just need to add the init function to register the error struct type.
E.g: https://play.golang.org/p/GAVhHG0m10W
Thanks, but I'm not asking to see the change you are proposing. I'm asking to see an example of a program that will be fixed by this change.
@ianlancetaylor Sure, I've created this sample code to showcase the use case where you want to return an error in a response struct but you get the EOF error (client) and the rpc: gob error encoding body: gob: type not registered for interface: errors.errorString (server) errors.
Thanks. That was much more complicated than I was expecting, and I can't run it to see the problem, but it does suggest this program:
package main
import (
"bytes"
"encoding/gob"
"errors"
"log"
)
type S struct {
Err error
}
func main() {
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
dec := gob.NewDecoder(&buf)
err := enc.Encode(S{errors.New("error")})
if err != nil {
log.Fatal(err)
}
var s S
err = dec.Decode(&s)
if err != nil {
log.Fatal(err)
}
}Running that program fails with
2018/01/05 15:28:02 gob: type not registered for interface: errors.errorString
which I take to be the kind of error you are discussing.
Change https://golang.org/cl/86456 mentions this issue: errors, encoding/gob: support gob transmission of errors.New
I think that if we decide to do this, the right approach is https://golang.org/cl/86456. That approach avoids importing encoding/gob in the errors package. We do not want the low level errors package to depend on the high level encoding/gob package.
Yes, your example is more simple to reproduce this issue because mine is the entire use case for RPC.
Agree with your implementation to avoid the encoding import.
FWIW, if we need to fix this in package errors, using encoding.BinaryMarshaler or encoding.TextMarshaler would be more generally applicable than the gob-specific interfaces (and will be picked up by gob).
Rather than teach gob about one particular way of making errors, a better plan might be to have gob implement a fallback for errors in general. If something implements the error interface, gob could arrange to transmit the string and recover the error value on the other side, automatically. For those who want richer, type-aware semantics for errors, for example as is done in https://github.com/upspin/upspin/tree/master/errors, one can always implement one of the standard marshaling interfaces for that type, which would override the default.
I've been thinking about doing this for a while; perhaps the time has come to do something.
fwiw, I'm not convinced it is needed. The loss of type will still surprise some, but a little more insidiously. The person who expects the real type after unmarshaling will get caught out... quietly.
Marshaled structs typically form part of a contract that crosses process boundaries, and these contracts should attempt to remain simple and or explicit. In the simple case I would rather see a string type in the contract and it populated by an err.Error() so as to avoid the looming disappointment. If more complex errors are to form part of the contract then I agree the explicit marshaller interface as per @robpike s upspin example is the better approach.
I would say the marshalling of errors is almost binary:
- The client of the unmarshaled error expects an error type, and if they do they will surely have, or soon have, more complex needs like wrapped errors (a.la. upspin)
- The error may as well be explicitly a
string
In case anecdotes are in any way useful. In 4 years of working with rpc contracts across 40 services this issue has come up once, and never wrt persistence (we do gob encode for persistence). We found it quickly because it failed loudly.
In the model I have, the special handling would occur only if the implementing type is not otherwise marshalable, which I think addresses your worry.
encoding/gob has been frozen for ages now and we aren't adding more features/support to it, thus I shall close this issue. So long encoding/gob, you served us well!