{"record":{"id":"abf61646b6fd132a","repo":"denoland/deno","slug":"task-name-filter-uses-an-exclusion-group","errorCode":null,"errorMessage":"task name filter '{}' uses an exclusion group '(!...)' but has no wildcard '*' to exclude from","messagePattern":"task name filter '(.+?)' uses an exclusion group '\\(!\\.\\.\\.\\)' but has no wildcard '\\*' to exclude from","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"cli/tools/task.rs","lineNumber":1581,"sourceCode":"\nfn arg_to_task_name_filter(\n  input: &str,\n) -> Result<TaskNameFilter<'_>, AnyError> {\n  // Parse an optional trailing exclusion group of the form `(!a|b|c)`.\n  // Exclusion values are matched against what each `*` in the pattern\n  // captures, e.g. `test:*(!e2e|interactive)` excludes `test:e2e` and\n  // `test:interactive` but still matches `test:unit`.\n  let (pattern, exclusions): (&str, Vec<&str>) = match input.rfind(\"(!\") {\n    Some(open) if input.ends_with(')') => {\n      let inner = &input[open + 2..input.len() - 1];\n      (&input[..open], inner.split('|').collect())\n    }\n    _ => (input, Vec::new()),\n  };\n\n  if !pattern.contains('*') {\n    if !exclusions.is_empty() {\n      return Err(anyhow!(\n        \"task name filter '{}' uses an exclusion group '(!...)' but has no wildcard '*' to exclude from\",\n        input\n      ));\n    }\n    return Ok(TaskNameFilter::Exact(input));\n  }\n\n  let mut regex_str = regex::escape(pattern);\n  regex_str = regex_str.replace(\"\\\\*\", \"(.*)\");\n  regex_str = format!(\"^{}\", regex_str);\n  let re = Regex::new(&regex_str)?;\n  let exclusions = exclusions.into_iter().map(String::from).collect();\n  Ok(TaskNameFilter::Regex { re, exclusions })\n}\n\n#[derive(Debug)]\nenum TaskNameFilter<'s> {\n  Exact(&'s str),","sourceCodeStart":1563,"sourceCodeEnd":1599,"githubUrl":"https://github.com/denoland/deno/blob/89f33cbef296a2b287f323d42de54c871fa69c77/cli/tools/task.rs#L1563-L1599","documentation":"`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.","triggerScenarios":"`deno task --filter 'name(!excluded)'` where the pattern before the `(!...)` group contains no `*` character.","commonSituations":"Assuming the exclusion syntax works on exact task names; typos dropping the `*`; adapting npm script-selection habits to deno task filters.","solutions":["Add the wildcard: use 'test*(!e2e)' instead of 'test(!e2e)'","Drop the exclusion group and use the exact task name when no wildcard is needed","Run `deno task` to see which names the pattern should match"],"exampleFix":"# before\ndeno task --filter 'test(!e2e)'\n# after\ndeno task --filter 'test*(!e2e)'","handlingStrategy":"validation","validationCode":"# reject --filter values that use (!...) without a *\nfilter=\"$1\"\ncase \"$filter\" in\n  *'(!'*)\n    case \"$filter\" in\n      *'*'*\"(!\"*) : ;;\n      *) echo \"filter '$filter' needs a '*' before the exclusion group\" >&2; exit 1 ;;\n    esac ;;\nesac","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Remember the exclusion syntax `(!a|b)` only applies to wildcard matches","Dry-check filter selection with `deno task` output before wiring into CI","Prefer exact names or simple globs unless exclusions are truly needed"],"tags":["task","filter","glob","cli","validation"],"backgroundTag":null,"analyzedSha":"89f33cbef296a2b287f323d42de54c871fa69c77","analyzedAt":"2026-08-16T07:54:21.310Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}