netbirdio/netbird · warning

invalid duration: {}

Error message

invalid duration: {}

What it means

The capture endpoint parses its duration parameter with time.ParseDuration, so the value must be a Go duration string such as 30s, 5m, or 1h30m. Malformed values like bare numbers, '10min', or '10seconds' return 400 with the ParseDuration error text appended.

Source

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

// handleCapture streams a pcap or text packet capture for the given client.
//
// Query params:
//
//	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 {

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Use Go duration syntax with s, m, or h suffixes: ?duration=30s or ?duration=5m
  2. Omit duration entirely to capture for the 30-minute default (maxCaptureDuration)
  3. Validate with time.ParseDuration client-side before sending

Example fix

// before
GET /debug/clients/<id>/capture?duration=10min

// after: Go duration units
GET /debug/clients/<id>/capture?duration=10m
Defensive patterns

Strategy: validation

Validate before calling

if _, err := time.ParseDuration(s); err != nil {
    return fmt.Errorf("duration must use Go units (s, m, h): %w", err)
}
// only then: GET /debug/clients/<id>/capture?duration=<s>

Type guard

func isGoDuration(s string) bool {
    _, err := time.ParseDuration(s)
    return err == nil
}

Prevention

When it happens

Trigger: GET /debug/clients/<id>/capture?duration=10 (no unit), ?duration=10min, ?duration=10seconds, or any string time.ParseDuration rejects.

Common situations: Assuming a bare number means seconds; carrying duration formats from other libraries (moment.js, Java) into the URL; forwarding an unvalidated free-text field from a UI.

Related errors


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