actualbudget/actual · error · APIError
transactions-import: accountId must be an id
Error message
transactions-import: accountId must be an id
What it means
Actual's transactions-import API validates its inputs up front and throws an APIError when accountId is not a string. Account ids in Actual are internal string identifiers; the check at app.ts:1645 runs before any reconciliation work, so callers passing null, undefined, a number, or an object are rejected immediately. It guards the rest of the import pipeline from operating on a malformed account reference.
Source
Thrown at packages/loot-core/src/server/accounts/app.ts:1646
export type ImportTransactionsResult = bankSync.ReconcileTransactionsResult & {
errors: Array<{
message: string;
}>;
};
async function importTransactions({
accountId,
transactions,
isPreview,
opts,
}: {
accountId: AccountEntity['id'];
transactions: ImportTransactionEntity[];
isPreview: boolean;
opts?: ImportTransactionsOpts;
}): Promise<ImportTransactionsResult> {
if (typeof accountId !== 'string') {
throw APIError('transactions-import: accountId must be an id');
}
const payeeNameNormalization = opts?.payeeNameNormalization ?? 'title-case';
if (!bankSync.PAYEE_NAME_NORMALIZATIONS.includes(payeeNameNormalization)) {
throw APIError(
`transactions-import: payeeNameNormalization must be one of ${bankSync.PAYEE_NAME_NORMALIZATIONS.join(
', ',
)}, got '${String(payeeNameNormalization)}'`,
);
}
try {
const reconciled = await bankSync.reconcileTransactions(
accountId,
transactions,
{
isPreview,
defaultCleared: opts?.defaultCleared,View on GitHub (pinned to d4334cb6e6)
Solutions
- Log and inspect the accountId value just before the call; ensure it comes from an Actual account entity's id field (a string).
- Resolve the account by name via the accounts/query API and use the returned id.
- Fix type mappings so numeric ids from external systems are converted to Actual string ids before calling.
- Wrap the call in try/catch on APIError and surface a clear validation message to the user.
Example fix
// before
await api.transactionsImport(bankAccount.rowId, txns);
// after
const acct = await api.qbi-query;
// resolve first:
const accounts = await api.getAccounts();
const acct = accounts.find(a => a.name === 'Checking');
if (!acct) throw new Error('Account not found');
await api.transactionsImport(acct.id, txns); Defensive patterns
Strategy: validation
Validate before calling
if (typeof accountId !== 'string' || accountId === '') {
throw new Error(`transactionsImport: expected string account id, got ${String(accountId)}`);
} Type guard
function isAccountId(v: unknown): v is string {
return typeof v === 'string' && v.length > 0;
} Try / catch
try {
await api.transactionsImport(accountId, transactions);
} catch (e) {
if (e instanceof APIError && e.message.includes('accountId must be an id')) {
// surface a validation message / re-resolve the account id
} else throw e;
} Prevention
- Always take accountId from an Actual account entity's id field, never from external DB rows.
- Resolve accounts by name through the query API instead of hardcoding ids.
- Add a pre-call assertion/validation of the id type in integration code.
- Log the id before calling to make mis-mapped fields obvious.
When it happens
Trigger: Calling importTransactions / api.transactions-import with accountId === null, undefined, a numeric id, an object like {id: ...}, or a value read from the wrong field of an account entity.
Common situations: API consumers mapping external bank account objects to Actual accounts and passing the wrong property; using a numeric internal DB row id instead of Actual's string id; a failed lookup returning undefined before the import call.
Related errors
- Unknown payee name normalization: ${String(normalization)}
- `date` is required when adding a transaction
- Amount is invalid, must be an integer: ${trans.amount}
- There is already a filter named ${item.name}
- Filter name is required
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/59e40ddf4d785568.
Report an issue: GitHub.