Go strings are not marshallers

So, you’ve built a nifty custom slog.Handler in Go. It’s dutifully processing your application’s logs, maybe even shipping them off to a service like BigQuery. Things are great, until… fatal error: stack overflow. What gives?

This was precisely the situation I found myself in. My CologHandler had a method, uploadToBigQuery, responsible for, well, uploading logs. Inside this method, I was iterating through log attributes. If an attribute wasn’t a json.Marshaler, I’d log a warning using slog.Warn. I was aware that the new slog would call itself and therefore also trigger a new upload. Since I converted the value, that wasn’t suitable for JSON marshalling, to a string, I thought everything was safe. Because strings can be marshalled to JSON, right? Well, not quite.

``go r.Attrs(func(a slog.Attr) bool { value, ok := a.Value.Any().(json.Marshaler) if ok { params[a.Key] = value } else { slog.Warn(“non-json-marshaler value in log attributes”, “key”, a.Key, “value”, a.Value.String()) } return true })


Primitive types, including strings, do not implement the `json.Marshaler` interface directly. They can be marshalled to JSON, but they don't explicitly declare that they can be marshalled.

This of course caused another call to `slog.Warn`, and another, and another... until the stack overflowed.