netbirdio/netbird · error

request_timeout must be positive for L4 services

Error message

request_timeout must be positive for L4 services

What it means

Returned by validateL4Target when target.options.request_timeout is negative. As with session_idle_timeout, the message overstates the rule: only values below zero are rejected, and zero means unset/default. These L4 timeouts bound how long a session/request may run on the passthrough, so a negative value has no meaning.

Source

Thrown at management/internals/modules/reverseproxy/service/service.go:1063

		}
	case TargetTypeSubnet:
		if target.Host == "" {
			return errors.New("target host is required for subnet targets")
		}
	case TargetTypeCluster:
		// target_id carries the cluster address; the proxy resolves
		// the upstream at request time.
	default:
		return fmt.Errorf("invalid target_type %q for L4 service", target.TargetType)
	}
	if target.Path != nil && *target.Path != "" && *target.Path != "/" {
		return errors.New("path is not supported for L4 services")
	}
	if target.Options.SessionIdleTimeout < 0 {
		return errors.New("session_idle_timeout must be positive for L4 services")
	}
	if target.Options.RequestTimeout < 0 {
		return errors.New("request_timeout must be positive for L4 services")
	}
	if target.Options.SkipTLSVerify {
		return errors.New("skip_tls_verify is not supported for L4 services")
	}
	if target.Options.PathRewrite != "" {
		return errors.New("path_rewrite is not supported for L4 services")
	}
	if len(target.Options.CustomHeaders) > 0 {
		return errors.New("custom_headers is not supported for L4 services")
	}
	return nil
}

// Service mode constants.
const (
	ModeHTTP = "http"
	ModeTCP  = "tcp"
	ModeUDP  = "udp"

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Set request_timeout to 0 (omit) for the default, or a positive duration for an explicit bound.
  2. Clamp computed durations in client code: if d < 0 { d = 0 }.
  3. Keep in mind both L4 timeouts share the same rule - fix them together when cleaning a template.

Example fix

// before
"options": { "request_timeout": -5000000000 }

// after
"options": { "request_timeout": 30000000000 }
Defensive patterns

Strategy: validation

Validate before calling

func checkL4RequestTimeout(o TargetOptions) error {
	if o.RequestTimeout < 0 {
		return errors.New("request_timeout must be >= 0 (0 = default)")
	}
	return nil
}

Type guard

func isL4RequestTimeoutValid(o TargetOptions) bool {
	return o.RequestTimeout >= 0
}

Try / catch

if err := svc.Validate(); err != nil {
	if strings.Contains(err.Error(), "request_timeout") {
		return respondBadRequest(errors.New("use 0 for default or a positive duration; negatives are invalid"))
	}
	return respondBadRequest(err)
}

Prevention

When it happens

Trigger: An L4 target with a negative request_timeout in the payload; a client computing request_timeout as a difference that can go negative; configs ported from tools where negative timeout means 'disabled'.

Common situations: Reusing -1 conventions from other proxy configs. Duration math without a lower clamp. Copy-paste between session_idle_timeout and request_timeout where one was intentionally negative for testing.

Understand the failure class

Related errors


AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16). Data as JSON: /api/errors/cb6ec6a62218bba2. Report an issue: GitHub.