{"record":{"id":"930cd2ded84960e7","repo":"actualbudget/actual","slug":"duplicate-filter-warning-conditions-already-exist","errorCode":null,"errorMessage":"Duplicate filter warning: conditions already exist. Filter name: ${condExists}","messagePattern":"Duplicate filter warning: conditions already exist\\. Filter name: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/filters/app.ts","lineNumber":132,"sourceCode":"  const item = {\n    id: filterId,\n    conditions: filter.state.conditions,\n    conditionsOp: filter.state.conditionsOp,\n    name: filter.state.name,\n  };\n\n  if (item.name) {\n    if (await filterNameExists(item.name, item.id, true)) {\n      throw new Error('There is already a filter named ' + item.name);\n    }\n  } else {\n    throw new Error('Filter name is required');\n  }\n\n  if (item.conditions.length > 0) {\n    const condExists = conditionExists(item, filter.filters, true);\n    if (condExists) {\n      throw new Error(\n        'Duplicate filter warning: conditions already exist. Filter name: ' +\n          condExists,\n      );\n    }\n  } else {\n    throw new Error('Conditions are required');\n  }\n\n  // Create the filter here based on the info\n  await db.insertWithSchema('transaction_filters', filterModel.fromJS(item));\n\n  return filterId;\n}\n\nasync function updateFilter(filter) {\n  const item = {\n    id: filter.state.id,\n    conditions: filter.state.conditions,","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/filters/app.ts#L114-L150","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Fetch existing filters first and check whether an identical conditions set already exists; reuse that filter instead of creating a new one.","Catch the error, parse the existing filter's name from the message, and inform the user which filter duplicates the conditions.","Vary the conditions (e.g. different value or an extra condition) if a genuinely distinct filter is intended.","Make scripts idempotent: search for the matching filter by conditions before creating."],"exampleFix":"// before\nawait aql.query('filter-create', { state: { name: 'Copy', conditions: dupConditions, conditionsOp: 'and' } });\n// after\nconst filters = await aql.query('filters');\nif (filters.some(f => JSON.stringify(f.conditions) === JSON.stringify(dupConditions))) {\n  return; // already exists, skip creation\n}\nawait aql.query('filter-create', { state: { name: 'Copy', conditions: dupConditions, conditionsOp: 'and' } });","handlingStrategy":"validation","validationCode":"const filters = await aql.query('filters');\nconst key = (c) => `${c.field}|${c.op}|${JSON.stringify(c.value)}|${JSON.stringify(c.options ?? {})}`;\nconst sig = (f) => [...f.conditions].map(key).sort().join(';') + '|' + (f.conditions.length > 1 ? f.conditionsOp : '');\nif (filters.some(f => !f.tombstone && sig(f) === sig(newState))) {\n  return; // duplicate conditions: reuse the existing filter instead\n}","typeGuard":null,"tryCatchPattern":"try {\n  await aql.query('filter-create', { state });\n} catch (e) {\n  if (e.message.startsWith('Duplicate filter warning')) {\n    const existingName = e.message.split('Filter name: ')[1];\n    // reuse or notify about existing filter\n  } else throw e;\n}","preventionTips":["Compare condition signatures against existing filters before creating","Make automated filter creation idempotent (search-then-create)","Only show the save action when conditions differ from all existing filters"],"tags":["filters","duplicate","conditions","validation"],"backgroundTag":"duplicate-resource-conditions","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}