{"record":{"id":"f4d7eec59db0441f","repo":"gastownhall/beads","slug":"invalid-timeout-v","errorCode":null,"errorMessage":"invalid timeout: %v","messagePattern":"invalid timeout: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/gate.go","lineNumber":400,"sourceCode":"\tgateType  string\n\treason    string\n\tawaitID   string\n\ttitleFlag string\n\ttimeout   time.Duration\n}\n\nfunc gatherGateCreateInput(cmd *cobra.Command) (gateCreateInput, error) {\n\tin := gateCreateInput{}\n\tin.blocksID, _ = cmd.Flags().GetString(\"blocks\")\n\tin.gateType, _ = cmd.Flags().GetString(\"type\")\n\tin.reason, _ = cmd.Flags().GetString(\"reason\")\n\tin.awaitID, _ = cmd.Flags().GetString(\"await-id\")\n\tin.titleFlag, _ = cmd.Flags().GetString(\"title\")\n\ttimeoutStr, _ := cmd.Flags().GetString(\"timeout\")\n\tif timeoutStr != \"\" {\n\t\tparsed, err := time.ParseDuration(timeoutStr)\n\t\tif err != nil {\n\t\t\treturn in, fmt.Errorf(\"invalid timeout: %v\", err)\n\t\t}\n\t\tin.timeout = parsed\n\t}\n\treturn in, nil\n}\n\n// buildGateIssue constructs the ad-hoc gate issue exactly the way the direct\n// route always has; the proxied route reuses it for the same reason the\n// renderers are shared.\nfunc buildGateIssue(in gateCreateInput, targetID string) *types.Issue {\n\ttitle := fmt.Sprintf(\"Gate: %s\", in.gateType)\n\tif in.awaitID != \"\" {\n\t\ttitle = fmt.Sprintf(\"Gate: %s %s\", in.gateType, in.awaitID)\n\t}\n\tif in.titleFlag != \"\" {\n\t\ttitle = in.titleFlag\n\t}\n","sourceCodeStart":382,"sourceCodeEnd":418,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/gate.go#L382-L418","documentation":"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\".","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nbd gate create --timeout 30        # invalid timeout: time: missing unit\n// after\nbd gate create --timeout 30s","handlingStrategy":"validation","validationCode":"# validate duration before invoking\ncase \"$TIMEOUT\" in\n  ''|*[!0-9hms]*) echo \"invalid duration: $TIMEOUT\" >&2; exit 1;;\nesac","typeGuard":null,"tryCatchPattern":"if _, err := time.ParseDuration(timeoutStr); err != nil {\n    return fmt.Errorf(\"--timeout must be a Go duration like 30s, 5m, 1h: %w\", err)\n}","preventionTips":["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."],"tags":["cli","flags","duration-parsing"],"backgroundTag":"invalid-duration","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}