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
- Use Go duration syntax with s, m, or h suffixes: ?duration=30s or ?duration=5m
- Omit duration entirely to capture for the 30-minute default (maxCaptureDuration)
- 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
- Always emit durations with time.Duration.String(), which produces valid units
- Never send a bare number or formats like 'min'/'seconds' from other ecosystems
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
- duration must not be negative
- value parameter is required
- invalid value %q: %v
- client not found
- host argument required
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/4f530db5ad48f974.
Report an issue: GitHub.