{"record":{"id":"40f956ee81d533d2","repo":"gastownhall/beads","slug":"invalid-closed-time-w","errorCode":null,"errorMessage":"invalid closed time: %w","messagePattern":"invalid closed time: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/query/evaluator.go","lineNumber":388,"sourceCode":"\tcase OpGreater:\n\t\tfilter.UpdatedAfter = &t\n\tcase OpGreaterEq:\n\t\tfilter.UpdatedAfter = &t\n\tcase OpLess:\n\t\tfilter.UpdatedBefore = &t\n\tcase OpLessEq:\n\t\tendOfDay := time.Date(t.Year(), t.Month(), t.Day(), 23, 59, 59, 999999999, t.Location())\n\t\tfilter.UpdatedBefore = &endOfDay\n\tdefault:\n\t\treturn fmt.Errorf(\"updated does not support %s operator\", comp.Op.String())\n\t}\n\treturn nil\n}\n\nfunc (e *Evaluator) applyClosedFilter(comp *ComparisonNode, filter *types.IssueFilter) error {\n\tt, err := e.parseTimeValue(comp)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"invalid closed time: %w\", err)\n\t}\n\tswitch comp.Op {\n\tcase OpGreater:\n\t\tfilter.ClosedAfter = &t\n\tcase OpGreaterEq:\n\t\tfilter.ClosedAfter = &t\n\tcase OpLess:\n\t\tfilter.ClosedBefore = &t\n\tcase OpLessEq:\n\t\tendOfDay := time.Date(t.Year(), t.Month(), t.Day(), 23, 59, 59, 999999999, t.Location())\n\t\tfilter.ClosedBefore = &endOfDay\n\tdefault:\n\t\treturn fmt.Errorf(\"closed does not support %s operator\", comp.Op.String())\n\t}\n\treturn nil\n}\n\nfunc (e *Evaluator) applyStartedFilter(comp *ComparisonNode, filter *types.IssueFilter) error {","sourceCodeStart":370,"sourceCodeEnd":406,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/query/evaluator.go#L370-L406","documentation":"Wraps a parseTimeValue failure for the closed field: the value of a `closed` comparison could not be parsed as a relative time or absolute timestamp. The raw parser error is preserved with %w. Note that closed filters only make sense for issues that have been closed; parsing happens before any operator dispatch.","triggerScenarios":"Queries like `closed > 30x`, `closed < ''`, `closed >= nonsense` — any `closed <op> value` whose value fails duration (parseDurationAgo) or relative-time (ParseRelativeTime) parsing.","commonSituations":"Reporting scripts with malformed interpolated dates; units the duration parser rejects; users typing prose dates (`closed > last month`) where only compact forms work; running closed filters in environments where relative keywords differ.","solutions":["Use a supported duration form: `closed > 30d`, `closed < 24h`","Use an absolute date/time accepted by ParseRelativeTime (e.g. `2026-08-01` or RFC3339)","Check the wrapped cause in the error chain to identify the exact parse problem"],"exampleFix":"// before\nbd list 'closed > one month ago'\n// after\nbd list 'closed > 30d'","handlingStrategy":"validation","validationCode":"// Validate the closed value parses before running the query\nfunc validClosedValue(v string) bool {\n\tif regexp.MustCompile(`^\\d+[dhmsw]$`).MatchString(v) {\n\t\treturn true\n\t}\n\t_, err := timeparsing.ParseRelativeTime(v, time.Now())\n\treturn err == nil\n}\nif !validClosedValue(value) { return fmt.Errorf(\"bad closed value: %q\", value) }","typeGuard":"func isParseableTime(v string) bool {\n\t_, err := timeparsing.ParseRelativeTime(v, time.Now())\n\treturn err == nil\n}","tryCatchPattern":"if err := eval.Eval(node); err != nil {\n\tif strings.Contains(err.Error(), \"invalid closed time\") {\n\t\t// fix the value (e.g. 'one month ago' -> '30d') and retry\n\t}\n}","preventionTips":["Use compact durations for closed filters: 30d, 7d","Use ISO dates for absolute cutoffs","Never interpolate prose date phrases into queries","Unwrap errors.As to the underlying parse error when debugging"],"tags":["query","time-parsing","closed","invalid-input"],"backgroundTag":"invalid-date-format","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}