{"record":{"id":"ce359fbfca3bffaf","repo":"actualbudget/actual","slug":"filter-name-is-required","errorCode":null,"errorMessage":"Filter name is required","messagePattern":"Filter name is required","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/filters/app.ts","lineNumber":126,"sourceCode":"\n  return keys1.every(key => opt1[key] === opt2[key]);\n}\n\nasync function createFilter(filter): Promise<TransactionFilterEntity['id']> {\n  const filterId = uuidv4();\n  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;","sourceCodeStart":108,"sourceCodeEnd":144,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/filters/app.ts#L108-L144","documentation":"createFilter requires a non-empty filter name. If filter.state.name is falsy (empty string, null, undefined), the guard `if (item.name)` fails and the code throws 'Filter name is required' instead of attempting the duplicate-name check. Saved transaction filters are addressed by name in the UI, so a name is mandatory.","triggerScenarios":"Calling the filter-create mutator with state.name === '' or missing; a UI form submitting before the user typed a name; a script/plugin constructing the filter payload programmatically and omitting the name field.","commonSituations":"Automated budget setup scripts that build filter payloads; custom UIs built on the Actual API forgetting the name field; empty-string names after trimming whitespace in custom frontends.","solutions":["Provide a non-empty name in filter.state before calling createFilter.","Validate the name client-side (trim and check length > 0) and disable submit until valid.","If the name came from user input, trim it and re-check; whitespace-only strings are still falsy after trim only if you trim yourself."],"exampleFix":"// before\nawait aql.query('filter-create', { state: { name: '', conditions, conditionsOp: 'and' } });\n// after\nconst name = userInput.trim();\nif (!name) throw new Error('Filter name is required');\nawait aql.query('filter-create', { state: { name, conditions, conditionsOp: 'and' } });","handlingStrategy":"validation","validationCode":"const name = (state.name ?? '').trim();\nif (!name) throw new Error('Filter name is required before calling filter-create');","typeGuard":"function hasFilterName(state: { name?: string | null }): state is { name: string } {\n  return typeof state.name === 'string' && state.name.trim().length > 0;\n}","tryCatchPattern":"try {\n  await aql.query('filter-create', { state });\n} catch (e) {\n  if (e.message === 'Filter name is required') {\n    // surface a required-field error to the user\n  } else throw e;\n}","preventionTips":["Validate name non-empty (after trim) before any filter-create call","Bind the save button to form validity so an empty name cannot submit","When building payloads in scripts, always set name explicitly"],"tags":["filters","validation","missing-field","api"],"backgroundTag":"missing-required-field","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}