gastownhall/beads · error
--pattern %q is not a valid glob: %v
Error message
--pattern %q is not a valid glob: %v
What it means
ValidateSweepRequest probes the IDPattern with filepath.Match and refuses malformed glob patterns. filepath.Match reports a malformed pattern on any subject, so one probe against the empty string classifies the pattern itself. This matters because front doors used to discard the error, turning a pattern like '[' into 'nothing matched' on a command whose job is to delete matches — a silent no-op on a destructive operation.
Source
Thrown at internal/workapi/sweep.go:46
// it lives HERE rather than in a CLI handler: a second front door inherits it
// by calling the role. See issueops.SweepRequest.
func ValidateSweepRequest(in issueops.SweepRequest) error {
switch in.Tier {
case issueops.SweepEphemeral, issueops.SweepDurable:
case "":
return fmt.Errorf("%w: sweep requires a tier (%q or %q), and has no default",
issueops.ErrValidation, issueops.SweepEphemeral, issueops.SweepDurable)
default:
return fmt.Errorf("%w: %q is not a sweep tier; use %q or %q",
issueops.ErrValidation, in.Tier, issueops.SweepEphemeral, issueops.SweepDurable)
}
if in.IDPattern != "" {
// filepath.Match reports a malformed pattern on any subject, so one
// probe against the empty string classifies the pattern itself. The
// front doors used to discard this error, which turned `--pattern '['`
// into "nothing matched" on a command whose job is to delete matches.
if _, err := filepath.Match(in.IDPattern, ""); err != nil {
return fmt.Errorf("%w: --pattern %q is not a valid glob: %v",
issueops.ErrValidation, in.IDPattern, err)
}
}
if in.Tier == issueops.SweepDurable && in.ClosedBefore == nil && in.IDPattern == "" {
return fmt.Errorf("%w: a durable sweep requires a closed-before cutoff or an id pattern; "+
"pass the pattern \"*\" to sweep every closed issue deliberately",
issueops.ErrValidation)
}
return nil
}
// BuildSweepCandidateFilter turns a sweep request into the storage-level
// filter that selects its CANDIDATES: the closed rows of one tier, bounded by
// the cutoff.
//
// The pattern is deliberately NOT in the filter. Globs are matched in Go
// (MatchesSweepPattern) because a LIKE translation would silently disagree with
// filepath.Match on `[...]` and on the escape rules — a disagreement that, onView on GitHub (pinned to 71377f2769)
Solutions
- Fix the glob pattern so filepath.Match accepts it (balance brackets, escape specials)
- Validate with filepath.Match(pattern, "") in the caller before constructing the request for an earlier, clearer failure
- Prefer the deliberate "*" pattern (or no pattern) over hand-built globs when sweeping everything closed
- Handle errors.Is(err, issueops.ErrValidation) and echo the underlying filepath.Match error
Example fix
// before
req := issueops.SweepRequest{Tier: issueops.SweepDurable, IDPattern: "bd-[0-9"}
// after
req := issueops.SweepRequest{Tier: issueops.SweepDurable, IDPattern: "bd-[0-9]*"} Defensive patterns
Strategy: validation
Validate before calling
if req.IDPattern != "" {
if _, err := filepath.Match(req.IDPattern, ""); err != nil {
return fmt.Errorf("pattern %q is not a valid glob: %v", req.IDPattern, err)
}
} Type guard
func isValidGlob(pattern string) bool {
_, err := filepath.Match(pattern, "")
return err == nil
} Try / catch
if err := sweeper.Sweep(ctx, req); err != nil {
if errors.Is(err, issueops.ErrValidation) {
return fmt.Errorf("bad sweep pattern: %w", err)
}
return err
} Prevention
- Pre-validate user globs with filepath.Match(pattern, "") at the CLI boundary
- Quote globs in shell usage docs so brackets are not consumed by the shell
- Prefer the literal "*" pattern over constructed globs when sweeping everything
- Remember a malformed pattern previously meant 'nothing matched' on a destructive command — never swallow filepath.Match errors
When it happens
Trigger: Calling a Sweeper with issueops.SweepRequest{Tier: ..., IDPattern: "["} or any other pattern filepath.Match rejects (unterminated character class, trailing backslash, etc.).
Common situations: A user typed --pattern '[' or 'bd-[0-9' on the command line; a shell ate a closing bracket leaving an unbalanced one; a caller interpolated an id prefix into a pattern template and broke the bracket syntax.
Related errors
- sweep requires a tier (%q or %q), and has no default
- %q is not a sweep tier; use %q or %q
- a durable sweep requires a closed-before cutoff or an id pat
- no store is open for this workspace
- not found
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/616acbaeef92ab35.
Report an issue: GitHub.