denoland/deno · error · anyhow::Error

task name filter '{}' uses an exclusion group '(!...)' but h

Error message

task name filter '{}' uses an exclusion group '(!...)' but has no wildcard '*' to exclude from

What it means

`deno task --filter` accepts glob patterns with exclusion groups, e.g. `test:*(!e2e|interactive)` runs every `test:*` task except the listed ones. The exclusion group is only meaningful when the pattern contains a wildcard to exclude from; a filter like `build(!lint)` has nothing to apply exclusions to, so the parser rejects it up front, echoing the input.

Source

Thrown at cli/tools/task.rs:1581

fn arg_to_task_name_filter(
  input: &str,
) -> Result<TaskNameFilter<'_>, AnyError> {
  // Parse an optional trailing exclusion group of the form `(!a|b|c)`.
  // Exclusion values are matched against what each `*` in the pattern
  // captures, e.g. `test:*(!e2e|interactive)` excludes `test:e2e` and
  // `test:interactive` but still matches `test:unit`.
  let (pattern, exclusions): (&str, Vec<&str>) = match input.rfind("(!") {
    Some(open) if input.ends_with(')') => {
      let inner = &input[open + 2..input.len() - 1];
      (&input[..open], inner.split('|').collect())
    }
    _ => (input, Vec::new()),
  };

  if !pattern.contains('*') {
    if !exclusions.is_empty() {
      return Err(anyhow!(
        "task name filter '{}' uses an exclusion group '(!...)' but has no wildcard '*' to exclude from",
        input
      ));
    }
    return Ok(TaskNameFilter::Exact(input));
  }

  let mut regex_str = regex::escape(pattern);
  regex_str = regex_str.replace("\\*", "(.*)");
  regex_str = format!("^{}", regex_str);
  let re = Regex::new(&regex_str)?;
  let exclusions = exclusions.into_iter().map(String::from).collect();
  Ok(TaskNameFilter::Regex { re, exclusions })
}

#[derive(Debug)]
enum TaskNameFilter<'s> {
  Exact(&'s str),

View on GitHub (pinned to 89f33cbef2)

Solutions

  1. Add the wildcard: use 'test*(!e2e)' instead of 'test(!e2e)'
  2. Drop the exclusion group and use the exact task name when no wildcard is needed
  3. Run `deno task` to see which names the pattern should match

Example fix

# before
deno task --filter 'test(!e2e)'
# after
deno task --filter 'test*(!e2e)'
Defensive patterns

Strategy: validation

Validate before calling

# reject --filter values that use (!...) without a *
filter="$1"
case "$filter" in
  *'(!'*)
    case "$filter" in
      *'*'*"(!"*) : ;;
      *) echo "filter '$filter' needs a '*' before the exclusion group" >&2; exit 1 ;;
    esac ;;
esac

Prevention

When it happens

Trigger: `deno task --filter 'name(!excluded)'` where the pattern before the `(!...)` group contains no `*` character.

Common situations: Assuming the exclusion syntax works on exact task names; typos dropping the `*`; adapting npm script-selection habits to deno task filters.

Related errors


AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16). Data as JSON: /api/errors/abf61646b6fd132a. Report an issue: GitHub.