netbirdio/netbird · warning

duration must not be negative

Error message

duration must not be negative

What it means

A duration that parses successfully but is negative is rejected explicitly: the handler checks d < 0 after time.ParseDuration. Zero is allowed and means 'use the 30-minute maximum'; only values below zero are refused.

Source

Thrown at proxy/internal/debug/handler.go:836

//	duration: capture duration (0 or absent = max, capped at 30m)
//	format:   "text" for human-readable output (default: pcap)
//	filter:   BPF-like filter expression (e.g. "host 10.0.0.1 and tcp port 443")
func (h *Handler) handleCapture(w http.ResponseWriter, r *http.Request, accountID types.AccountID) {
	client, ok := h.provider.GetClient(accountID)
	if !ok {
		http.Error(w, "client not found", http.StatusNotFound)
		return
	}

	duration := maxCaptureDuration
	if durationStr := r.URL.Query().Get("duration"); durationStr != "" {
		d, err := time.ParseDuration(durationStr)
		if err != nil {
			http.Error(w, "invalid duration: "+err.Error(), http.StatusBadRequest)
			return
		}
		if d < 0 {
			http.Error(w, "duration must not be negative", http.StatusBadRequest)
			return
		}
		if d > 0 {
			duration = min(d, maxCaptureDuration)
		}
	}

	filter := r.URL.Query().Get("filter")
	wantText := r.URL.Query().Get("format") == "text"
	verbose := r.URL.Query().Get("verbose") == "true"
	ascii := r.URL.Query().Get("ascii") == "true"

	opts := nbembed.CaptureOptions{Filter: filter, Verbose: verbose, ASCII: ascii}
	if wantText {
		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		opts.TextOutput = w
	} else {
		w.Header().Set("Content-Type", "application/vnd.tcpdump.pcap")

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Send a positive duration or omit the parameter
  2. Clamp computed durations to zero before sending: if d < 0 { d = 0 }
  3. Treat a negative remaining time in the caller as 'capture window already ended' and skip the request

Example fix

// before
d := deadline.Sub(time.Now()) // may be negative once the deadline passed
GET /debug/clients/<id>/capture?duration=<d>

// after
if d < 0 {
    d = 0 // zero means 'use the 30m maximum'
}
Defensive patterns

Strategy: validation

Validate before calling

d, err := time.ParseDuration(s)
if err != nil {
    return err
}
if d < 0 {
    d = 0 // zero selects the 30-minute maximum
}
q := url.Values{"duration": []string{d.String()}}

Type guard

func isNonNegativeDuration(s string) bool {
    d, err := time.ParseDuration(s)
    return err == nil && d >= 0
}

Prevention

When it happens

Trigger: GET /debug/clients/<id>/capture?duration=-1s or any negative Go duration string, e.g. a computed 'time until deadline' that already passed.

Common situations: Computing duration as deadline minus now, which goes negative once the deadline passes; a UI numeric field with a minus sign; sign errors in generated URLs.

Related errors


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