{"record":{"id":"72d2f85734894c22","repo":"actualbudget/actual","slug":"date-is-required-when-adding-a-transaction","errorCode":null,"errorMessage":"`date` is required when adding a transaction","messagePattern":"`date` is required when adding a transaction","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/accounts/sync.ts","lineNumber":452,"sourceCode":"}\n\nasync function normalizeTransactions(\n  transactions,\n  acctId,\n  {\n    payeeNameNormalization = 'title-case',\n  }: {\n    payeeNameNormalization?: PayeeNameNormalization;\n  } = {},\n) {\n  const payeesToCreate = new Map();\n\n  const normalized = [];\n  for (let trans of transactions) {\n    // Validate the date because we do some stuff with it. The db\n    // layer does better validation, but this will give nicer errors\n    if (trans.date == null) {\n      throw new Error('`date` is required when adding a transaction');\n    }\n\n    // Strip off the irregular properties\n    const { payee_name: originalPayeeName, subtransactions, ...rest } = trans;\n    trans = rest;\n\n    if (trans.amount != null && !Number.isInteger(trans.amount)) {\n      throw new TransactionError(\n        `Amount is invalid, must be an integer: ${trans.amount}`,\n      );\n    }\n\n    if (subtransactions) {\n      for (const sub of subtransactions) {\n        if (sub.amount != null && !Number.isInteger(sub.amount)) {\n          throw new TransactionError(\n            `Subtransaction amount is invalid, must be an integer: ${sub.amount}`,\n          );","sourceCodeStart":434,"sourceCodeEnd":470,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/accounts/sync.ts#L434-L470","documentation":"normalizeTransactions validates each incoming transaction before inserting it, because the sync code does date manipulation that requires a value. If a transaction has date == null (missing, undefined, or explicit null), the whole batch is rejected with this error before any writes. The db layer would validate too, but this early check gives a clearer message.","triggerScenarios":"Calling importTransactions/addTransactions (API or internal sync) with a transaction lacking a `date` field — e.g. { amount: -2500, payee_name: 'Kroger' } with no date key, or date explicitly null.","commonSituations":"CSV/OFX import scripts that fail to map a date column; API integrations building transactions from partial records with empty source dates; date parsers returning null on unparseable input and passing it through.","solutions":["Ensure every transaction has a `date` set to a valid date string (e.g. '2024-05-01').","Default missing dates upstream (e.g. to the statement date) instead of passing null.","Filter out or reject dateless rows before calling the API and report them.","Validate the whole batch client-side before submission."],"exampleFix":"// before\nawait actual.importTransactions(accId, [\n  { amount: -2500, payee_name: 'Kroger' } // no date\n]);\n// after\nawait actual.importTransactions(accId, [\n  { date: '2024-05-01', amount: -2500, payee_name: 'Kroger' }\n]);","handlingStrategy":"validation","validationCode":"const invalid = txs.filter(t => t.date == null);\nif (invalid.length) {\n  throw new Error(`All transactions need a date; ${invalid.length} are missing one`);\n}","typeGuard":"function hasDate(t: { date?: string | null }): t is { date: string } {\n  return t.date != null && t.date.length > 0;\n}","tryCatchPattern":"try {\n  await importTransactions(accountId, txs);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('`date` is required')) {\n    console.error('A transaction in the batch is missing a date; check your import mapping');\n    return;\n  }\n  throw e;\n}","preventionTips":["Validate every record's date before calling the API, not per-transaction after failure.","Default missing dates to a sensible value (e.g. statement date) in import scripts.","Check CSV/OFX column mapping so the date column populates `date`.","Handle unparseable dates at parse time instead of passing null through."],"tags":["validation","transactions","missing-field","api"],"backgroundTag":"missing-required-field","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}