47

I use the following to check for timeouts when calling a webservice, but I would like to check specifically if there is a timeout error returned. How do I do that :S

I have this:

// Timeout
type Timeout struct {
    Connect   time.Duration
    ReadWrite time.Duration
}

// TimeoutDialer
func TimeoutDialer(timeout *Timeout) func(net, addr string) (c net.Conn, err error) {
    return func(netw, addr string) (net.Conn, error) {    
        conn, err := net.DialTimeout(netw, addr, timeout.Connect)
        if err != nil {
            return nil, err
        }
        conn.SetDeadline(time.Now().Add(timeout.ReadWrite))
        return conn, nil
    }
}

// HttpClient
func HttpClient(config Config) *http.Client {
    to := &Timeout{
        Connect:   time.Duration(config.MaxWait) * time.Second,
        ReadWrite: time.Duration(config.MaxWait) * time.Second,
    }

    return &http.Client{
        Transport: &http.Transport{
            Dial: TimeoutDialer(to),
        },
    }
}
1
  • forgot to mention, you should turn off Keepalive in the Transport if you're going to set an absolute deadline on your connection. Otherwise you risk getting timeouts when reusing a connection.
    – JimB
    Commented May 6, 2014 at 22:37

4 Answers 4

93

If you are specifically looking for i/o timeouts, you can use errors.Is to detect an os.ErrDeadlineExceeded error as documented in the net package:

// If the deadline is exceeded a call to Read or Write or to other
// I/O methods will return an error that wraps os.ErrDeadlineExceeded.
// This can be tested using errors.Is(err, os.ErrDeadlineExceeded).
// The error's Timeout method will return true, but note that there
// are other possible errors for which the Timeout method will
// return true even if the deadline has not been exceeded.

if errors.Is(err, os.ErrDeadlineExceeded) {
...

All possible timeouts will still conform to the net.Error with Timeout() set properly. All you need to check for is:

if err, ok := err.(net.Error); ok && err.Timeout() {
2
  • Sorry, I should probably have been asserting a net.Error, since you could get Timeouts back that aren't an OpError.
    – JimB
    Commented May 6, 2014 at 16:17
  • 1
    So with http.Client.Timeout[go1.3+] it's impossible to check if error is timeout from Transport.CancelRequest? Is there a bug report for this? For me the message is totally confusing. Commented Mar 31, 2015 at 19:02
34

You can simply pass an error to os.IsTimeout() and if it's a timeout returned by net/http then it will return true.

func IsTimeout(err error) bool

IsTimeout returns a boolean indicating whether the error is known to report that a timeout occurred.

7
  • 9
    This should be the accepted answer - this is the simplest and most idiomatic way. Commented Jun 17, 2020 at 17:09
  • 1
    Although I appreciate the comment, "the most idiomatic way" is not and should never be accepted as a technical reason. Simple is better, however :)
    – Jimbo
    Commented Jun 17, 2020 at 17:55
  • 1
    A lot of people (including myself) learn Go (and other languages) by searching the internet, and they should be told the idiomatic way of doing something to learn the language more properly. A Go expert looking this up will immediately know which of the answers is the one he needs. Commented Jun 17, 2020 at 18:30
  • 1
    "Idiomatic Go" is nothing more than a style and it's incredibly dogmatic at that. In fact, often it's a language-agnostically poor practice. The correct way of doing something in programming is, most likely, entirely language agnostic. Use the right tool for the job, not what a bunch of purists tell you to do.
    – Jimbo
    Commented Jun 17, 2020 at 18:38
  • 3
    Make sure to also read the updated documentation: This function predates errors.Is, and the notion of whether an error indicates a timeout can be ambiguous. For example, the Unix error EWOULDBLOCK sometimes indicates a timeout and sometimes does not. New code should use errors.Is with a value appropriate to the call returning the error, such as os.ErrDeadlineExceeded. Commented Oct 7, 2022 at 3:23
15

You want the net.Error interface. http://golang.org/pkg/net/#Error

if e,ok := err.(net.Error); ok && e.Timeout() {
    // This was a timeout
} else if err != nil {
    // This was an error, but not a timeout
}

Note that the type assertion err.(net.Error) will correctly handle the nil case and return false for the ok value if nil is returned as the error, short-circuiting the Timeout check.

5
  • 1
    Unfortunately, trying response, err := client.Do(req) if e, ok := err.(net.Error); ok && e.Timeout() { // This was a timeout fmt.Println("timeout") return } gives me nothing :S Commented May 6, 2014 at 13:20
  • 1
    Are you sure it's a timeout error, then? you probably want to add a else if err != nil check to cover non-timeout errors.
    – Linear
    Commented May 6, 2014 at 13:26
  • I set the timeout (maxwait) to 2 seconds, and behind that a php script somewhere with a sleep of 10 seconds (thus this should be a timeout), the error returned is: Get api.vagrant/test.php: read tcp 192.168.33.10:80: i/o timeout Commented May 6, 2014 at 13:31
  • I'm not sure, DialTimeout should return a net.Error of some sort if it fails. I'm trying to read the source of the net package, but unfortunately low level networking code is system specific and thus a bit hard to find any proof of what is returned since it's buried under 30 function calls in different files.
    – Linear
    Commented May 6, 2014 at 13:53
  • 3
    Another way to check is to use os.IsTimeout() function. A working program can be find here - golangbyexample.com/net-http-timeout Commented Mar 18, 2019 at 13:25
4

It's also possible by doing something like this:

var timeoutError net.Error
if errors.As(err, &timeoutError); timeoutError.Timeout() {
    fmt.Println("timeout")
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.