actualbudget/actual · error

Duplicate filter warning: conditions already exist. Filter n

Error message

Duplicate filter warning: conditions already exist. Filter name: ${condExists}

What it means

When creating a filter, conditionExists(item, filter.filters, true) compares the new filter's conditions (field, op, value, options, and conditionsOp when more than one condition) against all existing non-deleted filters. If an existing filter has an identical condition set, its name is embedded in this 'Duplicate filter warning' error. This prevents saving two saved filters that are functionally identical.

Source

Thrown at packages/loot-core/src/server/filters/app.ts:132

  const item = {
    id: filterId,
    conditions: filter.state.conditions,
    conditionsOp: filter.state.conditionsOp,
    name: filter.state.name,
  };

  if (item.name) {
    if (await filterNameExists(item.name, item.id, true)) {
      throw new Error('There is already a filter named ' + item.name);
    }
  } else {
    throw new Error('Filter name is required');
  }

  if (item.conditions.length > 0) {
    const condExists = conditionExists(item, filter.filters, true);
    if (condExists) {
      throw new Error(
        'Duplicate filter warning: conditions already exist. Filter name: ' +
          condExists,
      );
    }
  } else {
    throw new Error('Conditions are required');
  }

  // Create the filter here based on the info
  await db.insertWithSchema('transaction_filters', filterModel.fromJS(item));

  return filterId;
}

async function updateFilter(filter) {
  const item = {
    id: filter.state.id,
    conditions: filter.state.conditions,

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Fetch existing filters first and check whether an identical conditions set already exists; reuse that filter instead of creating a new one.
  2. Catch the error, parse the existing filter's name from the message, and inform the user which filter duplicates the conditions.
  3. Vary the conditions (e.g. different value or an extra condition) if a genuinely distinct filter is intended.
  4. Make scripts idempotent: search for the matching filter by conditions before creating.

Example fix

// before
await aql.query('filter-create', { state: { name: 'Copy', conditions: dupConditions, conditionsOp: 'and' } });
// after
const filters = await aql.query('filters');
if (filters.some(f => JSON.stringify(f.conditions) === JSON.stringify(dupConditions))) {
  return; // already exists, skip creation
}
await aql.query('filter-create', { state: { name: 'Copy', conditions: dupConditions, conditionsOp: 'and' } });
Defensive patterns

Strategy: validation

Validate before calling

const filters = await aql.query('filters');
const key = (c) => `${c.field}|${c.op}|${JSON.stringify(c.value)}|${JSON.stringify(c.options ?? {})}`;
const sig = (f) => [...f.conditions].map(key).sort().join(';') + '|' + (f.conditions.length > 1 ? f.conditionsOp : '');
if (filters.some(f => !f.tombstone && sig(f) === sig(newState))) {
  return; // duplicate conditions: reuse the existing filter instead
}

Try / catch

try {
  await aql.query('filter-create', { state });
} catch (e) {
  if (e.message.startsWith('Duplicate filter warning')) {
    const existingName = e.message.split('Filter name: ')[1];
    // reuse or notify about existing filter
  } else throw e;
}

Prevention

When it happens

Trigger: Calling filter-create with a conditions array that exactly matches (same field/op/value/options, same length, and same conditionsOp for multi-condition filters) an existing active filter's conditions. Comparison is strict equality on values, so identical semantics with different strings (e.g. different date) do NOT collide.

Common situations: Users re-saving a filter they already created; automated scripts idempotently re-running filter-create with the same payload after a prior successful run; restoring budgets where a filter already exists.

Related errors


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