{"record":{"id":"c29d1ea31fefadc2","repo":"actualbudget/actual","slug":"failed-creating-a-new-rule","errorCode":null,"errorMessage":"Failed creating a new rule","messagePattern":"Failed creating a new rule","errorType":"exception","errorClass":"APIError","httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/api.ts","lineNumber":898,"sourceCode":"  return handlers['payees-get-nearby']({ latitude, longitude, maxDistance });\n};\n\nhandlers['api/rules-get'] = async function () {\n  checkFileOpen();\n  return handlers['rules-get']();\n};\n\nhandlers['api/payee-rules-get'] = async function ({ id }) {\n  checkFileOpen();\n  return handlers['payees-get-rules']({ id });\n};\n\nhandlers['api/rule-create'] = withMutation(async function ({ rule }) {\n  checkFileOpen();\n  const addedRule = await handlers['rule-add'](ruleModel.fromExternal(rule));\n\n  if ('error' in addedRule) {\n    throw APIError('Failed creating a new rule', addedRule.error);\n  }\n\n  return addedRule;\n});\n\nhandlers['api/rule-update'] = withMutation(async function ({ rule }) {\n  checkFileOpen();\n  const updatedRule = await handlers['rule-update'](\n    ruleModel.fromExternal(rule),\n  );\n\n  if ('error' in updatedRule) {\n    throw APIError('Failed updating the rule', updatedRule.error);\n  }\n\n  return updatedRule;\n});\n","sourceCodeStart":880,"sourceCodeEnd":916,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/api.ts#L880-L916","documentation":"api.createRule() delegates to the internal rule-add handler, which validates the rule (conditions, actions, condition structure). If the internal handler returns { error }, the API wraps it in this APIError with the original error attached.","triggerScenarios":"Calling api.createRule(rule) with a rule whose conditions/actions reference unknown fields, invalid operators (e.g. wrong value type for the operator), or a malformed conditions object.","commonSituations":"Hand-constructed rule payloads in scripts; rules exported from an older Actual version with operators no longer supported; typos in field names like 'payee' vs 'payee_name'.","solutions":["Inspect the attached addedRule.error (the APIError's cause) for the underlying validation message","Build the rule object with a valid conditionOp/conditions shape per docs — verify field names and operator/value compatibility","Test the same rule in the UI Rules editor; if the UI rejects it too, fix the rule, not the API call"],"exampleFix":"// before\nawait api.createRule({ conditions: [{ field: 'payee_name', op: 'contains', value: 123 }] });\n// after\nawait api.createRule({ conditions: [{ field: 'payee_name', op: 'contains', value: 'Store' }], actions: [{ field: 'category', value: 'Groceries' }], operation: 'and' });","handlingStrategy":"try-catch","validationCode":"const parsed = ruleModel.fromExternal(rule);\nconst res = await handlers['rule-add'](parsed);\nif ('error' in res) preflightCheck(res.error);","typeGuard":"function isRuleError(r) {\n  return r != null && typeof r === 'object' && 'error' in r;\n}","tryCatchPattern":"const added = await ruleAdd(rule);\nif (isRuleError(added)) {\n  throw new APIError('Failed creating a new rule', added.error);\n}","preventionTips":["Validate rule shape against the current RuleEntity model before insert","Surface the wrapped cause to callers for debugging","Add fixtures covering each operator/field combo in CI"],"tags":["api","rules","validation"],"backgroundTag":"entity-validation-failed","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}