denoland/deno · error · AnyError

Invalid filter

Error message

Invalid filter "{input}"

What it means

Thrown by `deno outdated` / `deno pm outdated` when a string passed via `--filters` cannot be parsed into a package pattern plus optional version requirement. The parser splits the filter on '@' (special-casing '@scope/name@version') and feeds the version part to `deno_semver`'s `VersionReq::parse_from_specifier`; anything that fails semver parsing, or a filter with an empty pattern, produces this error with the raw input echoed back.

Solutions

  1. Correct the separator: use `@` between name and version, e.g. `--filters "foo@^2.0.0"` instead of `"foo=^2.0.0"`.
  2. Remove or fix the version segment so it is a valid semver range accepted by Deno (`^1.2.3`, `~1.2`, `1.x`, `*`); bare `foo` with no version is also valid.
  3. For scoped packages keep the full form `@scope/pkg@^1.0.0`; a leading '!' negates (`!@scope/pkg`).
  4. Use '*' as a wildcard inside the pattern (`foo*`), never inside the version segment.

Example fix

# before
deno outdated --filters "foo=^2.0.0"

# after
deno outdated --filters "foo@^2.0.0"
Defensive patterns

Strategy: validation

Validate before calling

const FILTER_RE = /^!?(@[\w.-]+\/)?[\w.*-]+(@[^@\s]+)?$/;
function assertValidFilter(filter: string): void {
  if (!FILTER_RE.test(filter)) {
    throw new Error(`Invalid filter "${filter}"`);
  }
}

Type guard

function isValidFilter(f: string): boolean {
  return /^!?(@[\w.-]+\/)?[\w.*-]+(@[^@\s]+)?$/.test(f);
}

Try / catch

try {
  await runDeno(['outdated', `--filters=${filter}`]);
} catch (e) {
  if (String(e.message).includes('Invalid filter')) {
    // show the expected '<name>[@<semver-range>]' syntax to the user
  }
}

Prevention

When it happens

Trigger: Running `deno outdated --filters "foo=^2.0.0"` (using '=' instead of '@'); a version segment that is not a valid semver range such as `@2.` or `@latest`; a filter like `"@"` where split('@') yields no pattern; a scoped filter `"@scope/pkg@not-a-version"`. The same message is also attached as context when the pattern fails regex compilation.

Common situations: Muscle memory from npm/jest CLI syntax (`pkg=^2`), copying a filter from another tool that uses commas differently, or typos in the version range. Note that `--filters` takes the version after '@', not after '=' or ':'.

Understand the failure class

Background: "Unknown argument", "Invalid value", and "must be one of": invalid CLI argument errors explained — this error's family across 35 libraries.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/b45bc1a61e4f0f52. Report an issue: GitHub.

Appendix: source

Thrown at cli/tools/pm/outdated/mod.rs:753

        if let Some(scope_name) = s.strip_prefix('@') {
          if let Some(idx) = scope_name.find('@') {
            let (pattern, version_spec) = s.split_at(idx + 1);
            (
              pattern,
              Some(
                VersionReq::parse_from_specifier(
                  version_spec.trim_start_matches('@'),
                )
                .with_context(|| format!("Invalid filter \"{input}\""))?,
              ),
            )
          } else {
            (s, None)
          }
        } else {
          let mut parts = s.split('@');
          let Some(pattern) = parts.next() else {
            return Err(anyhow!("Invalid filter \"{input}\""));
          };
          (
            pattern,
            parts
              .next()
              .map(VersionReq::parse_from_specifier)
              .transpose()
              .with_context(|| format!("Invalid filter \"{input}\""))?,
          )
        };

      Ok(Filter {
        kind,
        regex: pattern_to_regex(pattern)
          .with_context(|| format!("Invalid filter \"{input}\""))?,
        version_spec,
      })
    }

View on GitHub (pinned to 9ad36f7a2c)