{"record":{"id":"cfa0b9db707ff582","repo":"actualbudget/actual","slug":"msg","errorCode":null,"errorMessage":"${msg}","messagePattern":"\\$\\{msg\\}","errorType":"validation","errorClass":"RuleError","httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/rules/rule-utils.ts","lineNumber":14,"sourceCode":"// @ts-strict-ignore\nimport * as dateFns from 'date-fns';\n\nimport { logger } from '#platform/server/log';\nimport { RuleError } from '#server/errors';\nimport { RSchedule } from '#server/util/rschedule';\nimport { recurConfigToRSchedule } from '#shared/schedules';\nimport type { RuleConditionEntity } from '#types/models';\n\nimport type { Rule } from './rule';\n\nexport function assert(test: unknown, type: string, msg: string): asserts test {\n  if (!test) {\n    throw new RuleError(type, msg);\n  }\n}\n\nconst OP_SCORES: Record<RuleConditionEntity['op'], number> = {\n  is: 10,\n  isNot: 10,\n  oneOf: 9,\n  notOneOf: 9,\n  isapprox: 5,\n  isbetween: 5,\n  gt: 1,\n  gte: 1,\n  lt: 1,\n  lte: 1,\n  contains: 0,\n  doesNotContain: 0,\n  matches: 0,\n  hasTags: 0,","sourceCodeStart":1,"sourceCodeEnd":32,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/rules/rule-utils.ts#L1-L32","documentation":"The `assert` helper in rule-utils.ts throws a `RuleError` whenever a falsy test value is passed, with a caller-supplied `type` and message. It is the standard invariant-check used throughout the rules engine (rule construction, condition parsing, test execution). Any rule definition or serialized rule data that violates the rules API contract will surface here.","triggerScenarios":"Any call to `assert(value, type, msg)` where `value` is falsy — e.g. `runTest` on a rule with a malformed condition, `new Rule(...)` with missing fields, or `parse` of a rule with an invalid operator/field combination.","commonSituations":"Programmatically building Rule objects via the API with missing `op`, `field`, or `value`; deserializing rules from an older/edited budget database where condition shapes changed; typos in custom rule conditions.","solutions":["Log the full rule/condition object being processed to identify which invariant failed","Validate rule conditions (op, field, value all present and valid) before calling runRules/new Rule","Migrate or fix malformed rule rows in the budget database","Upgrade to the latest version in case the rule schema changed between versions"],"exampleFix":"// before\nnew Rule({ conditions: [{ op: 'is' }] }); // missing field/value -> assert throws\n// after\nnew Rule({\n  conditions: [{ field: 'payee', op: 'is', value: 'amazon' }],\n  actions: [{ op: 'set', field: 'category', value: 'Shopping' }],\n});","handlingStrategy":"try-catch","validationCode":"function isValidRuleInput(rule) {\n  return (\n    Array.isArray(rule.conditions) && rule.conditions.length > 0 &&\n    rule.conditions.every(c => c.op && c.field) &&\n    Array.isArray(rule.actions) && rule.actions.every(a => a.op && a.field)\n  );\n}\nif (!isValidRuleInput(input)) throw new Error('Invalid rule input');","typeGuard":"function isRuleCondition(c) {\n  return typeof c === 'object' && c !== null &&\n    typeof c.field === 'string' && typeof c.op === 'string' &&\n    'value' in c;\n}","tryCatchPattern":"import { RuleError } from './rule-error';\ntry {\n  const rule = new Rule(rawRule);\n  rule.runTest(transaction);\n} catch (e) {\n  if (e instanceof RuleError) {\n    console.error(`Rule error [${e.type}]: ${e.message}`);\n  } else {\n    throw e;\n  }\n}","preventionTips":["Validate condition/action objects (op, field, value) before constructing a Rule","Build rules via typed helpers instead of raw objects","Round-trip test serialized rules with parse/runTest in unit tests","Keep budget data schema in sync with the app version before loading"],"tags":["rules-engine","validation","invariant"],"backgroundTag":"rule-condition-validation-failed","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}