{"record":{"id":"6eb774daa0192d8c","repo":"actualbudget/actual","slug":"invalid-splits-option-for-transactions-split","errorCode":null,"errorMessage":"Invalid \"splits\" option for transactions: \"${splitType}\"","messagePattern":"Invalid \"splits\" option for transactions: \"(.+?)\"","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/aql/schema/executors.ts","lineNumber":55,"sourceCode":"// q('transactions', { splits: \"grouped\" }).select({ $count: 'id' })\n//\n// The first will return the count of non-split and child\n// transactions, and the second will return the count of all parent\n// (or non-split) transactions\n\nfunction execTransactions(\n  compilerState: CompilerState,\n  queryState: QueryState,\n  sqlPieces: SqlPieces,\n  params: (string | number)[],\n  outputTypes: OutputTypes,\n) {\n  const tableOptions = queryState.tableOptions || {};\n  const splitType = tableOptions.splits\n    ? (tableOptions.splits as string)\n    : 'inline';\n  if (!isValidSplitsOption(splitType)) {\n    throw new Error(`Invalid \"splits\" option for transactions: \"${splitType}\"`);\n  }\n\n  if (splitType === 'all' || splitType === 'inline' || splitType === 'none') {\n    return execTransactionsBasic(\n      compilerState,\n      queryState,\n      sqlPieces,\n      params,\n      splitType,\n      outputTypes,\n    );\n  } else if (splitType === 'grouped') {\n    return execTransactionsGrouped(\n      compilerState,\n      queryState,\n      sqlPieces,\n      params,\n      outputTypes,","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/aql/schema/executors.ts#L37-L73","documentation":"execTransactions validates the optional 'splits' table option, which must be one of 'all' | 'inline' | 'none' | 'grouped'. Any other value passed when constructing the query is rejected before SQL runs.","triggerScenarios":"q('transactions', { splits: 'grouped ' }) or { splits: 'parent' } — any string not exactly matching a valid option.","commonSituations":"Typos, capitalization ('Grouped'), trailing whitespace, or passing user/CLI supplied strings without validation.","solutions":["Use one of the four valid values: 'all', 'inline', 'none', 'grouped'.","Validate user-supplied option strings against isValidSplitsOption before building the query.","Omit the option entirely if you want the default 'inline' behavior."],"exampleFix":"// before\nq('transactions', { splits: 'group' });\n// after\nq('transactions', { splits: 'grouped' });","handlingStrategy":"validation","validationCode":"const SPLITS_OPTIONS = ['all','inline','none','grouped'];\nfunction validateSplits(o) { if (o && !SPLITS_OPTIONS.includes(o)) throw new Error(`splits must be one of ${SPLITS_OPTIONS.join(', ')}`); }","typeGuard":"function isSplitsOption(v: unknown): v is 'all'|'inline'|'none'|'grouped' {\n  return typeof v === 'string' && ['all','inline','none','grouped'].includes(v);\n}","tryCatchPattern":"try {\n  return await q('transactions', { splits }).select('*').execute();\n} catch (e) {\n  if (e.message.includes('Invalid \"splits\" option')) {\n    return q('transactions').select('*').execute(); // fallback to default\n  }\n  throw e;\n}","preventionTips":["Use a const literal union instead of raw strings for the option","Trim/lowercase user-supplied options before validating","Omit the option when the default behavior is acceptable"],"tags":["aql","query","invalid-option"],"backgroundTag":"invalid-query-option","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}