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

  1. Use a Go duration format with a unit: --timeout 30s, 5m, 1h.
  2. If you meant seconds, append "s" to the bare number (30 -> 30s).
  3. Check the wrapped message for the exact offset of the parse failure (e.g. 'time: missing unit in duration "30"').
  4. 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

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

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/f4d7eec59db0441f. Report an issue: GitHub.