slimtoolkit/slim · error

setting read timeout: %v

Error message

setting read timeout: %v

What it means

After dialing the FastCGI backend, RoundTrip applies the transport's ReadTimeout via fcgiBackend.SetReadTimeout. If the connection cannot accept the timeout (typically because the connection is already broken/closed), the error is wrapped as "setting read timeout: %v". The backend conn is usable only if both read and write timeouts apply cleanly.

Source

Thrown at pkg/app/master/probe/http/internal/fastcgi.go:97

	network, address := "tcp", r.URL.Host

	if log.IsLevelEnabled(log.DebugLevel) {
		envJSON, _ := json.Marshal(env)
		log.Debugf("HTTP probe - FastCGI env - %s", string(envJSON))
	}

	ctx := r.Context()
	dialer := net.Dialer{Timeout: t.DialTimeout}
	fcgiBackend, err := DialWithDialerContext(ctx, network, address, dialer)
	if err != nil {
		// TODO: wrap in a special error type if the dial failed, so retries can happen if enabled
		return nil, fmt.Errorf("dialing backend: %v", err)
	}
	// fcgiBackend gets closed when response body is closed (see clientCloser)

	// read/write timeouts
	if err := fcgiBackend.SetReadTimeout(t.ReadTimeout); err != nil {
		return nil, fmt.Errorf("setting read timeout: %v", err)
	}
	if err := fcgiBackend.SetWriteTimeout(t.WriteTimeout); err != nil {
		return nil, fmt.Errorf("setting write timeout: %v", err)
	}

	contentLength := r.ContentLength
	if contentLength == 0 {
		contentLength, _ = strconv.ParseInt(r.Header.Get("Content-Length"), 10, 64)
	}

	var resp *http.Response
	switch r.Method {
	case http.MethodHead:
		resp, err = fcgiBackend.Head(env)
	case http.MethodGet:
		resp, err = fcgiBackend.Get(env, r.Body, contentLength)
	case http.MethodOptions:
		resp, err = fcgiBackend.Options(env)

View on GitHub (pinned to 81940d17fa)

Solutions

  1. Inspect the wrapped error for an os.ErrDeadlineUnsupported / 'not supported' cause and remove or adjust ReadTimeout if deadlines aren't supported.
  2. Check the backend for immediate disconnects (php-fpm logs, container restarts).
  3. Retest connectivity — this often masks a race where the backend died between dial and timeout setup.
  4. Set a sane positive ReadTimeout (not zero/negative) on the transport.

Example fix

// before
t := FastCGITransport{ DialTimeout: time.Second } // ReadTimeout unset/zero
// after
t := FastCGITransport{ DialTimeout: time.Second, ReadTimeout: 30 * time.Second, WriteTimeout: 30 * time.Second }
Defensive patterns

Strategy: retry

Validate before calling

// only fail on genuinely unsupported deadlines
if err := fcgiBackend.SetReadTimeout(t.ReadTimeout); err != nil {
    if errors.Is(err, os.ErrDeadlineUnsupported) {
        log.Warn("deadlines unsupported on this conn; continuing")
    } else {
        return err
    }
}

Try / catch

resp, err := fcgiTransport.RoundTrip(req)
if err != nil && strings.Contains(err.Error(), "setting read timeout") {
    if !errors.Is(errors.Unwrap(err), os.ErrDeadlineUnsupported) {
        return retryRoundTrip(req) // likely transient conn death
    }
}

Prevention

When it happens

Trigger: RoundTrip where SetReadTimeout fails right after a successful dial — usually the underlying conn is dead (peer reset immediately), or the conn type does not support deadline setting (e.g. certain mock/unix conns).

Common situations: Backend accepting then instantly closing the connection (crashing php-fpm worker); DialTimeout succeeding but the conn being torn down before deadlines are set; unusual network stacks where deadlines are unsupported.

Understand the failure class

Related errors


AI-assisted analysis of slimtoolkit/slim@81940d17fa (2026-08-31). Data as JSON: /api/errors/ce1742954740bfb4. Report an issue: GitHub.