gastownhall/beads · error
invalid timeout: %v
Error message
invalid timeout: %v
What it means
gatherGateCreateInput in cmd/bd/gate.go parses the --timeout flag with time.ParseDuration. If the string is non-empty but not a valid Go duration (e.g. "30", "5min", "abc"), the parse error is wrapped as 'invalid timeout: %v'. Valid values require a unit suffix, like "30s", "5m", "1h30m".
Source
Thrown at cmd/bd/gate.go:400
gateType string
reason string
awaitID string
titleFlag string
timeout time.Duration
}
func gatherGateCreateInput(cmd *cobra.Command) (gateCreateInput, error) {
in := gateCreateInput{}
in.blocksID, _ = cmd.Flags().GetString("blocks")
in.gateType, _ = cmd.Flags().GetString("type")
in.reason, _ = cmd.Flags().GetString("reason")
in.awaitID, _ = cmd.Flags().GetString("await-id")
in.titleFlag, _ = cmd.Flags().GetString("title")
timeoutStr, _ := cmd.Flags().GetString("timeout")
if timeoutStr != "" {
parsed, err := time.ParseDuration(timeoutStr)
if err != nil {
return in, fmt.Errorf("invalid timeout: %v", err)
}
in.timeout = parsed
}
return in, nil
}
// buildGateIssue constructs the ad-hoc gate issue exactly the way the direct
// route always has; the proxied route reuses it for the same reason the
// renderers are shared.
func buildGateIssue(in gateCreateInput, targetID string) *types.Issue {
title := fmt.Sprintf("Gate: %s", in.gateType)
if in.awaitID != "" {
title = fmt.Sprintf("Gate: %s %s", in.gateType, in.awaitID)
}
if in.titleFlag != "" {
title = in.titleFlag
}
View on GitHub (pinned to 71377f2769)
Solutions
- Use a Go duration format with a unit: --timeout 30s, 5m, 1h.
- If you meant seconds, append "s" to the bare number (30 -> 30s).
- Check the wrapped message for the exact offset of the parse failure (e.g. 'time: missing unit in duration "30"').
- In scripts, quote and normalize the value: "${TIMEOUT}s" when it's a plain integer.
Example fix
// before bd gate create --timeout 30 # invalid timeout: time: missing unit // after bd gate create --timeout 30s
Defensive patterns
Strategy: validation
Validate before calling
# validate duration before invoking case "$TIMEOUT" in ''|*[!0-9hms]*) echo "invalid duration: $TIMEOUT" >&2; exit 1;; esac
Try / catch
if _, err := time.ParseDuration(timeoutStr); err != nil {
return fmt.Errorf("--timeout must be a Go duration like 30s, 5m, 1h: %w", err)
} Prevention
- Always include a unit suffix: 30s, 5m, 1h — never a bare number.
- Remember Go duration syntax: no "min"/"sec" abbreviations, use m/s.
- Quote interpolated values in scripts to avoid stray whitespace.
- Add a shell-level duration regex check before calling bd.
When it happens
Trigger: Running gate create with --timeout set to a string without a duration unit ("--timeout 30"), a misspelled unit ("--timeout 5mins"), or any unparseable value. Empty string is allowed and skips parsing.
Common situations: Users passing a bare number assuming seconds; translating from tools that accept "5min" or "30sec"; scripts interpolating values with hidden whitespace or stray characters; copying timeouts from cron-style or ISO-8601 formats.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- cannot use both --pull-only and --push-only
- %w (--prefer-local, --prefer-ado, --prefer-newer)
- cannot specify both --reason-file and --reason/--resolution/
- invalid --dolt-auto-commit=%q (valid: off, on, batch)
- failed to open file: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/f4d7eec59db0441f.
Report an issue: GitHub.