actualbudget/actual · error · Error

--where and --filter are mutually exclusive

Error message

--where and --filter are mutually exclusive

What it means

The Actual CLI's `actual query` command lets you build an AQL query either inline with --where/--select flags or via a structured --filter expression. These two filtering mechanisms are mutually exclusive, so buildQueryFromFlags throws when both --where and --filter are supplied on the same invocation. It is an early validation guard before any query is executed against the budget.

Source

Thrown at packages/cli/src/commands/query.ts:172

    if (cmdOpts.limit) {
      throw new Error('--last and --limit are mutually exclusive');
    }
  }

  const table =
    cmdOpts.table ?? (last !== undefined ? 'transactions' : undefined);
  if (!table) {
    throw new Error('--table is required (or use --file or --last)');
  }

  if (!(table in TABLE_SCHEMA)) {
    throw new Error(
      `Unknown table "${table}". Available tables: ${AVAILABLE_TABLES}`,
    );
  }

  if (cmdOpts.where && cmdOpts.filter) {
    throw new Error('--where and --filter are mutually exclusive');
  }

  if (cmdOpts.count && cmdOpts.select) {
    throw new Error('--count and --select are mutually exclusive');
  }

  let queryObj = api.q(table);

  if (cmdOpts.count) {
    queryObj = queryObj.calculate({ $count: '*' });
  } else if (cmdOpts.select) {
    queryObj = queryObj.select(cmdOpts.select.split(','));
  } else if (last !== undefined) {
    queryObj = queryObj.select(LAST_DEFAULT_SELECT);
  }

  const filterStr = cmdOpts.filter ?? cmdOpts.where;
  if (filterStr) {

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Remove the --where flag and express the condition inside --filter, or vice versa
  2. Convert the --where expression into the equivalent AQL filter JSON: e.g. --where 'amount>0' becomes --filter '{"amount":{"$gt":0}}'
  3. If a wrapper script sets --filter, drop your manual --where or edit the wrapper

Example fix

// before
actual query transactions --where "amount>0" --filter '{"date":{"$gte":"2024-01-01"}}'

// after (merge into one filter)
actual query transactions --filter '{"$and":[{"amount":{"$gt":0}},{"date":{"$gte":"2024-01-01"}}]}'
Defensive patterns

Strategy: validation

Validate before calling

// in the script that assembles CLI args
const args = ['query', 'transactions'];
if (whereExpr) args.push('--where', whereExpr);
if (filterJson) args.push('--filter', filterJson);
if (whereExpr && filterJson) {
  throw new Error('Pass either --where or --filter, not both');
}

Type guard

function hasExclusiveFlags(opts: { where?: string; filter?: string }): boolean {
  return opts.where !== undefined && opts.filter !== undefined;
}

Try / catch

try {
  await run(['actual', 'query', 'transactions', ...flags]);
} catch (e) {
  if (String(e.message).includes('mutually exclusive')) {
    console.error('Drop either --where or --filter');
  } else throw e;
}

Prevention

When it happens

Trigger: Running `actual query transactions --where 'amount>0' --filter '{"date":{"$gte":"2024-01-01"}}'` — both flags set on the same command. Also happens in scripts that pass flags conditionally (e.g. a wrapper always appends --filter while the user also passes --where).

Common situations: Users combining examples from different docs pages; shell scripts accumulating flags from multiple config sources; copy-pasting a filter expression into an existing command that already had a --where clause.

Related errors


AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29). Data as JSON: /api/errors/537b37a99277f8c3. Report an issue: GitHub.