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.
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)
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
- Invalid --name: must be a non-empty string.
- No update fields provided. Use --name or --offbudget.
- Invalid cutoff date: expected a valid date (e.g. YYYY-MM-DD)
- No update fields provided. Use --name or --hidden.
- No update fields provided. Use --name or --hidden.
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/cfa0b9db707ff582.
Report an issue: GitHub.