actualbudget/actual · error

accountId and startDate must either both be arrays or both b

Error message

accountId and startDate must either both be arrays or both be strings

What it means

The SimpleFIN app requires accountId and startDate to be consistent types: either both a single string or both arrays (batch mode). If one is an array and the other is not, it throws 'accountId and startDate must either both be arrays or both be strings' after logging both values. This validation protects the batch-fetch logic that zips the two arrays together.

Source

Thrown at packages/sync-server/src/app-simplefin/app-simplefin.js:105

  }),
);

app.post(
  '/transactions',
  handleError(async (req, res) => {
    const { accountId, startDate } = req.body || {};

    const accessKey = secretsService.get(SecretName.simplefin_accessKey);

    if (isInvalidAccessKey(accessKey)) {
      invalidToken(res);
      return;
    }

    if (Array.isArray(accountId) !== Array.isArray(startDate)) {
      console.log({ accountId, startDate });
      throw new Error(
        'accountId and startDate must either both be arrays or both be strings',
      );
    }
    if (Array.isArray(accountId) && accountId.length !== startDate.length) {
      console.log({ accountId, startDate });
      throw new Error('accountId and startDate arrays must be the same length');
    }

    const earliestStartDate = Array.isArray(startDate)
      ? startDate.reduce((a, b) => (a < b ? a : b))
      : startDate;
    let results;
    try {
      results = await getTransactions(
        accessKey,
        Array.isArray(accountId) ? accountId : [accountId],
        new Date(earliestStartDate),
      );
    } catch (e) {

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Pass matching shapes: wrap startDate in an array with one entry per account, or pass both as plain strings.
  2. For multiple accounts with different start dates, send an array of start dates in the same order as the account ids.
  3. For a single account, ensure both values are plain strings.
  4. Validate the payload shape client-side before calling the API.

Example fix

// before
const res = await fetch(base + '/simplefin/transactions', { method: 'POST', body: JSON.stringify({ accountId: ['a1','a2'], startDate: '2024-01-01' }) });

// after
const res = await fetch(base + '/simplefin/transactions', { method: 'POST', body: JSON.stringify({ accountId: ['a1','a2'], startDate: ['2024-01-01','2024-01-01'] }) });
Defensive patterns

Strategy: validation

Validate before calling

if (Array.isArray(accountId) !== Array.isArray(startDate)) {
  throw new Error('accountId and startDate must have matching shapes (both array or both string)');
}

Prevention

When it happens

Trigger: Calling the SimpleFIN getTransactions endpoint/handler passing accountId as an array with startDate as a string (or vice versa), e.g. requesting multiple accounts but a single shared start date.

Common situations: Custom scripts or API integrations build a list of accounts but pass one date string; older clients using the single-account shape partially migrated to the batch shape; JSON payloads where startDate was serialized as a scalar by mistake.

Related errors


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