isJSONDecodeError reports whether err is a JSON decode failure at the SDK boundary, matching both typed json errors and their fmt.Errorf- wrapped substring form. io.EOF is intentionally excluded — at the SDK boundary an EOF is a transport failure, not a payload-shape failure.
(err error)
| 114 | // wrapped substring form. io.EOF is intentionally excluded — at the SDK |
| 115 | // boundary an EOF is a transport failure, not a payload-shape failure. |
| 116 | func isJSONDecodeError(err error) bool { |
| 117 | var syntaxErr *json.SyntaxError |
| 118 | var unmarshalTypeErr *json.UnmarshalTypeError |
| 119 | if errors.As(err, &syntaxErr) || errors.As(err, &unmarshalTypeErr) { |
| 120 | return true |
| 121 | } |
| 122 | |
| 123 | // Substring fallback for fmt.Errorf-wrapped json decode errors that no |
| 124 | // longer satisfy errors.As against the typed json errors. "invalid |
| 125 | // character" alone is too broad (other libraries surface it for non- |
| 126 | // JSON failures), so it is gated on the message also containing "json". |
| 127 | msg := err.Error() |
| 128 | if strings.Contains(msg, "unexpected end of JSON input") || |
| 129 | strings.Contains(msg, "cannot unmarshal") { |
| 130 | return true |
| 131 | } |
| 132 | lower := strings.ToLower(msg) |
| 133 | return strings.Contains(lower, "invalid character") && strings.Contains(lower, "json") |
| 134 | } |
no test coverage detected