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
- Pass matching shapes: wrap startDate in an array with one entry per account, or pass both as plain strings.
- For multiple accounts with different start dates, send an array of start dates in the same order as the account ids.
- For a single account, ensure both values are plain strings.
- 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
- Build request payloads with one helper that enforces paired shapes.
- When batching, always map accounts to their own start dates.
- Write a small client-side schema check (e.g. zod) before calling the endpoint.
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
- accountId and startDate arrays must be the same length
- Invalid access key
- 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)
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/5d3b7b7d7f9150e0.
Report an issue: GitHub.