rtk-ai/rtk · error

rtk find does not support compound predicates or actions…

Error message

rtk find does not support compound predicates or actions (e.g. -not, -exec). Use `find` directly.

What it means

RTK's find replacement (parse_find_args in src/cmds/system/find_cmd.rs) supports only simple searches: its compact syntax (`rtk find '*.rs' [path] [-m max] [-t type]`) and single-predicate native syntax (`-name`, `-type`, `-maxdepth`, ...). Before parsing it scans argv for UNSUPPORTED_FIND_FLAGS — `-not`, `!`, `-or`, `-o`, `-and`, `-a`, `-exec`, `-execdir`, `-delete`, `-print0`, `-newer`, and friends — and aborts on any exact-token match. This is a deliberate scope limit: silently mishandling compound predicates or side-effecting actions would return wrong or destructive results, so rtk refuses and points you at the real `find`.

Solutions

  1. Run the system `find` directly for compound predicates or actions; wrap with `rtk proxy find ...` if you still want it counted in rtk's usage stats.
  2. Rewrite as a simple predicate rtk supports: `rtk find '*.rs' -t f` or `rtk find . -name '*.rs' -type f -maxdepth 3`.
  3. For -exec/-delete effects, use find itself or a small shell loop — rtk deliberately avoids reproducing side-effecting actions.

Example fix

# before
rtk find . -name '*.tmp' -not -name '*.keep' -delete

# after — use the real find (rtk supports neither -not nor -delete)
find . -name '*.tmp' -not -name '*.keep' -delete
# or keep it in rtk usage stats:
rtk proxy find . -name '*.tmp' -not -name '*.keep' -delete
Defensive patterns

Strategy: validation

Validate before calling

# bash — pre-check the tokens rtk find rejects before invoking it
UNSUPPORTED='-not ! -or -o -and -a -exec -execdir -delete -print0 -newer'
for a in "$@"; do
  case " $UNSUPPORTED " in
    *" $a "*) exec find "$@" ;;   # compound predicate -> real find
  esac
done
rtk find "$@"

Type guard

rtk_find_safe() {
  # returns 0 when no argv token is in rtk find's unsupported-flag set
  local a
  for a in "$@"; do
    case " $a " in
      ' -not '|' ! '|' -or '|' -o '|' -and '|' -a '|' -exec '|' -execdir '|' -delete '|' -print0 '|' -newer ') return 1 ;;
    esac
  done
  return 0
}

Try / catch

out=$(rtk find "$@" 2>&1) || {
  case "$out" in
    *'compound predicates'*) exec find "$@" ;;  # deliberate scope limit -> raw find
  esac
  printf '%s\n' "$out" >&2; exit 1
}
printf '%s\n' "$out"

Prevention

When it happens

Trigger: Any `rtk find` invocation containing a token from UNSUPPORTED_FIND_FLAGS, e.g. `rtk find . -name '*.rs' -not -name '*_test.rs'`, `rtk find . -exec rm {} \;`, `rtk find . -newer ref -type f`, `rtk find . -name '*.tmp' -delete`.

Common situations: Agents piping every find through rtk by habit; muscle-memory GNU find idioms with -not/-exec; scripts assuming rtk find is a drop-in find replacement.

Related errors


AI-assisted analysis of rtk-ai/rtk@29f9bb7161 (2026-08-20). Data as JSON: /api/errors/be3ba4f8b2f5b626. Report an issue: GitHub.

Appendix: source

Thrown at src/cmds/system/find_cmd.rs:89

    "-regex", "-iregex",
];

fn has_unsupported_find_flags(args: &[String]) -> bool {
    args.iter()
        .any(|a| UNSUPPORTED_FIND_FLAGS.contains(&a.as_str()))
}

/// Parse arguments from raw args vec, supporting both native find and RTK syntax.
///
/// Native find syntax: `find . -name "*.rs" -type f -maxdepth 3`
/// RTK syntax: `find *.rs [path] [-m max] [-t type]`
fn parse_find_args(args: &[String]) -> Result<FindArgs> {
    if args.is_empty() {
        return Ok(FindArgs::default());
    }

    if has_unsupported_find_flags(args) {
        anyhow::bail!(
            "rtk find does not support compound predicates or actions (e.g. -not, -exec). Use `find` directly."
        );
    }

    if has_native_find_flags(args) {
        parse_native_find_args(args)
    } else {
        parse_rtk_find_args(args)
    }
}

/// Parse native find syntax: `find [path] -name "*.rs" -type f -maxdepth 3`
fn parse_native_find_args(args: &[String]) -> Result<FindArgs> {
    let mut parsed = FindArgs::default();
    let mut i = 0;

    // First non-flag argument is the path (standard find behavior)
    if !args[0].starts_with('-') {

View on GitHub (pinned to 29f9bb7161)