actualbudget/actual · error · RuleError
${msg}
Error message
${msg} What it means
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.
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
Example fix
// before
new Rule({ conditions: [{ op: 'is' }] }); // missing field/value -> assert throws
// after
new Rule({
conditions: [{ field: 'payee', op: 'is', value: 'amazon' }],
actions: [{ op: 'set', field: 'category', value: 'Shopping' }],
}); Defensive patterns
Strategy: try-catch
Validate before calling
function isValidRuleInput(rule) {
return (
Array.isArray(rule.conditions) && rule.conditions.length > 0 &&
rule.conditions.every(c => c.op && c.field) &&
Array.isArray(rule.actions) && rule.actions.every(a => a.op && a.field)
);
}
if (!isValidRuleInput(input)) throw new Error('Invalid rule input'); Type guard
function isRuleCondition(c) {
return typeof c === 'object' && c !== null &&
typeof c.field === 'string' && typeof c.op === 'string' &&
'value' in c;
} Try / catch
import { RuleError } from './rule-error';
try {
const rule = new Rule(rawRule);
rule.runTest(transaction);
} catch (e) {
if (e instanceof RuleError) {
console.error(`Rule error [${e.type}]: ${e.message}`);
} else {
throw e;
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- A date condition is required to create a schedule
- A tag with that name already exists
- Account group name is required
- Account mismatch: transaction belongs to account
- Account with ID not found.
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/cfa0b9db707ff582.
Report an issue: GitHub.
Appendix: source
Thrown at packages/loot-core/src/server/rules/rule-utils.ts:14
// @ts-strict-ignore
import * as dateFns from 'date-fns';
import { logger } from '#platform/server/log';
import { RuleError } from '#server/errors';
import { RSchedule } from '#server/util/rschedule';
import { recurConfigToRSchedule } from '#shared/schedules';
import type { RuleConditionEntity } from '#types/models';
import type { Rule } from './rule';
export function assert(test: unknown, type: string, msg: string): asserts test {
if (!test) {
throw new RuleError(type, msg);
}
}
const OP_SCORES: Record<RuleConditionEntity['op'], number> = {
is: 10,
isNot: 10,
oneOf: 9,
notOneOf: 9,
isapprox: 5,
isbetween: 5,
gt: 1,
gte: 1,
lt: 1,
lte: 1,
contains: 0,
doesNotContain: 0,
matches: 0,
hasTags: 0,View on GitHub (pinned to d4334cb6e6)