{"record":{"id":"5d3b7b7d7f9150e0","repo":"actualbudget/actual","slug":"accountid-and-startdate-must-either-both-be-arrays","errorCode":null,"errorMessage":"accountId and startDate must either both be arrays or both be strings","messagePattern":"accountId and startDate must either both be arrays or both be strings","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/sync-server/src/app-simplefin/app-simplefin.js","lineNumber":105,"sourceCode":"  }),\n);\n\napp.post(\n  '/transactions',\n  handleError(async (req, res) => {\n    const { accountId, startDate } = req.body || {};\n\n    const accessKey = secretsService.get(SecretName.simplefin_accessKey);\n\n    if (isInvalidAccessKey(accessKey)) {\n      invalidToken(res);\n      return;\n    }\n\n    if (Array.isArray(accountId) !== Array.isArray(startDate)) {\n      console.log({ accountId, startDate });\n      throw new Error(\n        'accountId and startDate must either both be arrays or both be strings',\n      );\n    }\n    if (Array.isArray(accountId) && accountId.length !== startDate.length) {\n      console.log({ accountId, startDate });\n      throw new Error('accountId and startDate arrays must be the same length');\n    }\n\n    const earliestStartDate = Array.isArray(startDate)\n      ? startDate.reduce((a, b) => (a < b ? a : b))\n      : startDate;\n    let results;\n    try {\n      results = await getTransactions(\n        accessKey,\n        Array.isArray(accountId) ? accountId : [accountId],\n        new Date(earliestStartDate),\n      );\n    } catch (e) {","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/sync-server/src/app-simplefin/app-simplefin.js#L87-L123","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nconst res = await fetch(base + '/simplefin/transactions', { method: 'POST', body: JSON.stringify({ accountId: ['a1','a2'], startDate: '2024-01-01' }) });\n\n// after\nconst res = await fetch(base + '/simplefin/transactions', { method: 'POST', body: JSON.stringify({ accountId: ['a1','a2'], startDate: ['2024-01-01','2024-01-01'] }) });","handlingStrategy":"validation","validationCode":"if (Array.isArray(accountId) !== Array.isArray(startDate)) {\n  throw new Error('accountId and startDate must have matching shapes (both array or both string)');\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["simplefin","validation","api-misuse"],"backgroundTag":"parameter-validation-failed","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}