actualbudget/actual · error · ValidationError

${name} is missing field ${String(field)}

Error message

${name} is missing field ${String(field)}

What it means

requiredFields validates that a model row contains all required fields before database writes via validate(). For inserts (update=false) every listed field must exist and be non-null; for updates, a present field must not be null. Violations throw a ValidationError naming the model and the missing field, e.g. 'transactions is missing field account'.

Source

Thrown at packages/loot-core/src/server/models.ts:32

import type {
  DbAccount,
  DbAccountGroup,
  DbCategory,
  DbCategoryGroup,
  DbPayee,
} from './db';
import { ValidationError } from './errors';

export function requiredFields<T extends object, K extends keyof T>(
  name: string,
  row: T,
  fields: K[],
  update?: boolean,
) {
  fields.forEach(field => {
    if (update) {
      if (row.hasOwnProperty(field) && row[field] == null) {
        throw new ValidationError(`${name} is missing field ${String(field)}`);
      }
    } else {
      if (!row.hasOwnProperty(field) || row[field] == null) {
        throw new ValidationError(`${name} is missing field ${String(field)}`);
      }
    }
  });
}

export function toDateRepr(str: string) {
  if (typeof str !== 'string') {
    throw new Error('toDateRepr not passed a string: ' + str);
  }

  return parseInt(str.replace(/-/g, ''));
}

export function fromDateRepr(number: number) {

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Read the validation message to see which model and field failed, then include that field with a valid non-null value in your call.
  2. For transaction adds, always pass account (id or name resolvable), date, and amount.
  3. For updates, omit fields you don't intend to change instead of sending null.
  4. If you believe the field should be optional, check the model definition in packages/loot-core/src/server/models.ts to confirm the required list before filing an issue.

Example fix

// before
await api.addTransactions('My Account', [{ date: '2026-08-28', amount: -1234 }]);
// error: transactions is missing field account (id mismatch) — resolve by name to id
// after
const acct = await api.getAccount('My Account');
await api.addTransactions(acct.id, [{ date: '2026-08-28', amount: -1234 }]);
Defensive patterns

Strategy: try-catch

Validate before calling

const REQUIRED = { transactions: ['account','date','amount'] };
function validateRow(model, row) {
  for (const f of REQUIRED[model] || []) {
    if (row?.[f] == null) throw new ValidationError(`${model} is missing field ${f}`);
  }
}

Type guard

function hasRequiredFields(row, fields) {
  return typeof row === 'object' && row !== null &&
    fields.every(f => f in row && row[f] != null);
}

Try / catch

try {
  await api.addTransactions(accountId, txs);
} catch (e) {
  if (e.name === 'ValidationError' && e.message.includes('is missing field')) {
    const field = e.message.split('missing field ')[1];
    // supply the missing field and retry
  } else throw e;
}

Prevention

When it happens

Trigger: Calling model APIs (e.g. transactions-add, account-create, schedule or payee creation via the API/app) with an object that omits a required field or explicitly sets it to null/undefined — such as adding a transaction without account, or creating an account without name/balance fields.

Common situations: Scripting the @actual-app/api and forgetting mandatory fields; custom importers building rows incompletely; plugins or automation passing partial update objects where a null slipped in; version changes making a previously optional field required.

Understand the failure class

Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.

Related errors


AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29). Data as JSON: /api/errors/db326dab468291f0. Report an issue: GitHub.