DocsHTTPErrors

Errors

Tell a transport failure apart from an HTTP error status, and handle failures on a stream.

The client draws a clear line between a request that could not be made and a request that came back with an error status. Knowing which is which is the whole error model.

A returned error means the request failed

A verb returns a non-nil error only when the request could not be built, sent, or read. A failure to join the URL, a refused connection, a canceled context, or a body that could not be read all surface as the returned error, already wrapped with context.

resp, err := client.Get(ctx, http.GetRequest{Request: http.Request{Path: "/users/1"}})
if err != nil {
	// the request never completed, a connection, timeout, or malformed URL
	return fmt.Errorf("failed to fetch user: %w", err)
}

An error status is a normal response

An HTTP status such as 404 or 500 is a completed request, so it comes back as a *Response with a nil error. Check resp.StatusCode yourself and read resp.Body for the server's error payload.

resp, err := client.Get(ctx, http.GetRequest{Request: http.Request{Path: "/users/1"}})
if err != nil {
	return err
}
if resp.StatusCode >= 400 {
	return fmt.Errorf("user lookup returned %d: %s", resp.StatusCode, resp.Body)
}

Note

This split keeps the two kinds of failure from being confused. A transport error is never a valid response, and a 4xx or 5xx is never a Go error, so a caller always knows which one it is handling.

Errors on a stream

A streaming call reports failure in two places. GetStream and PostStream return an error when the initial request fails or comes back with a non-OK status. The same failure, and any read error partway through, also arrives as a final StreamResponseTypeEOF frame whose Error is set.

stream := make(chan http.StreamResponse)
if err := client.GetStream(ctx, stream, http.Request{Path: "/events"}); err != nil {
	return err
}
for ev := range stream {
	if ev.Type == http.StreamResponseTypeEOF && ev.Error != nil {
		return ev.Error
	}
	// handle ev
}

A clean end of stream is a StreamResponseTypeEOF frame with a nil Error. See Streaming for the full frame loop.