{"record":{"id":"0bc8fed1e667ed07","repo":"actualbudget/actual","slug":"formula-must-start-with","errorCode":null,"errorMessage":"Formula must start with =","messagePattern":"Formula must start with =","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/rules/action.ts","lineNumber":301,"sourceCode":"      for (const value of Object.values(typedNode)) {\n        if (typeof value === 'object' && value !== null) {\n          walk(value);\n        }\n      }\n    };\n\n    walk(ast);\n    return variables;\n  }\n\n  executeFormulaSync(\n    formula: string,\n    transaction: Partial<TransactionForRules>,\n  ): unknown {\n    let hfInstance: ReturnType<typeof HyperFormula.buildEmpty> | null = null;\n\n    if (!formula || !formula.startsWith('=')) {\n      throw new Error('Formula must start with =');\n    }\n\n    try {\n      hfInstance = HyperFormula.buildEmpty({\n        licenseKey: 'gpl-v3',\n        language: 'enUS',\n        dateFormats: ['DD/MM/YYYY', 'YYYY-MM-DD', 'YYYY/MM/DD'],\n        context: {\n          balanceOfPrefetch: transaction['_balanceOfPrefetched'] ?? new Map(),\n        },\n      });\n\n      const sheetName = hfInstance.addSheet('Sheet1');\n      const sheetId = hfInstance.getSheetId(sheetName);\n\n      if (sheetId === undefined) {\n        throw new Error('Failed to create sheet');\n      }","sourceCodeStart":283,"sourceCodeEnd":319,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/rules/action.ts#L283-L319","documentation":"executeFormulaSync evaluates rule action formulas via HyperFormula, which requires formulas in spreadsheet syntax. If the formula string is empty or does not start with '=', it cannot be parsed as a formula, so the function throws immediately before building the HyperFormula instance.","triggerScenarios":"A rule action (set/change value with a formula) whose stored formula is '' or lacks the leading '=' — e.g. user typed '1+2' or 'A1*2' without '=', or a value/formula toggle mixed up plain values with formulas.","commonSituations":"Users typing plain numbers or expressions into formula fields; migrated/legacy rules where the '=' prefix was stripped; form code saving the raw input without normalization.","solutions":["Ensure the formula string begins with '=' (e.g. '=1+2', '=date(today)') before executing the rule","Normalize input when saving the rule: prepend '=' if missing or reject at save time","Verify the rule's valueIsFormula/conditions flags so plain values aren't routed to the formula evaluator"],"exampleFix":"// before\nconst formula = input.trim(); // \"1+2\"\n// after\nconst formula = input.trim().startsWith('=') ? input.trim() : '=' + input.trim();","handlingStrategy":"validation","validationCode":"if (!formula || !formula.startsWith('=')) throw new Error('Formula must start with =');\nexecuteFormulaSync(formula, transaction);","typeGuard":"function isFormula(value: unknown): value is string {\n  return typeof value === 'string' && value.trim().startsWith('=');\n}","tryCatchPattern":"try {\n  const result = executeFormulaSync(formula, transaction);\n} catch (e) {\n  if (e instanceof Error && e.message === 'Formula must start with =') {\n    // treat input as a plain value or re-prompt the user\n  } else throw e;\n}","preventionTips":["Normalize formula input at save time to always include a leading '='","Distinguish plain values from formulas with an explicit flag in rule actions","Validate formulas in the rule editor UI before persisting"],"tags":["validation","formula","rules"],"backgroundTag":"formula-syntax-error","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}