{"record":{"id":"23e90d61c24b1d15","repo":"actualbudget/actual","slug":"query-file-must-contain-a-json-object","errorCode":null,"errorMessage":"Query file must contain a JSON object","messagePattern":"Query file must contain a JSON object","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cli/src/commands/query.ts","lineNumber":309,"sourceCode":"    )\n    .option('--count', 'Count matching rows instead of returning them')\n    .option(\n      '--group-by <fields>',\n      'Comma-separated fields to group by (use with aggregate selects)',\n    )\n    .option(\n      '--file <path>',\n      'Read full query object from JSON file (use - for stdin)',\n    )\n    .addHelpText('after', RUN_EXAMPLES)\n    .action(async cmdOpts => {\n      const opts = program.opts();\n      await withConnection(\n        opts,\n        async () => {\n          const parsed = cmdOpts.file ? readJsonInput(cmdOpts) : undefined;\n          if (parsed !== undefined && !isRecord(parsed)) {\n            throw new Error('Query file must contain a JSON object');\n          }\n          const queryObj = parsed\n            ? buildQueryFromFile(parsed, cmdOpts.table)\n            : buildQueryFromFlags(cmdOpts);\n\n          const result = await api.aqlQuery(queryObj);\n\n          if (!isRecord(result) || !('data' in result)) {\n            throw new Error('Query result missing data');\n          }\n\n          if (cmdOpts.count) {\n            printOutput({ count: result.data }, opts.format);\n          } else {\n            printOutput(result.data, opts.format);\n          }\n        },\n        { mutates: false },","sourceCodeStart":291,"sourceCodeEnd":327,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/cli/src/commands/query.ts#L291-L327","documentation":"When `actual query --file <path>` (or piped JSON via stdin) is used, the CLI parses the input as JSON with readJsonInput and requires the top-level value to be a JSON object (the AQL query itself). A top-level array, string, number, or null is rejected with this error by registerQueryCommand before any query executes.","triggerScenarios":"Passing a file whose contents are a JSON array like `[{...}]`; piping a bare JSON array or scalar into `actual query --file -`; a file containing `null`; a file that stores a list of queries rather than one query object.","commonSituations":"Exporting query logs that wrap the query in an array; hand-editing a query file and accidentally changing the outer braces to brackets; tools like jq re-serializing output that was itself an array of results.","solutions":["Ensure the file's top level is a single JSON object: { \"table\": \"transactions\", \"select\": [...] }","If you have an array, index into it or unwrap it (e.g. jq '.[0]') before passing","Validate the file: `jq type file.json` must print \"object\"","Check the file is not empty or truncated"],"exampleFix":"// before (query.json)\n[\n  { \"table\": \"transactions\", \"select\": [\"date\"] }\n]\n\n// after\n{\n  \"table\": \"transactions\",\n  \"select\": [\"date\"]\n}","handlingStrategy":"type-guard","validationCode":"const raw = JSON.parse(readFileSync(queryFile, 'utf8'));\nif (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {\n  throw new Error(`${queryFile} must contain a single JSON object (the AQL query)`);\n}","typeGuard":"function isQueryObject(v: unknown): v is Record<string, unknown> {\n  return typeof v === 'object' && v !== null && !Array.isArray(v);\n}","tryCatchPattern":"try {\n  await run(['actual', 'query', '--file', queryFile]);\n} catch (e) {\n  if (String(e.message).includes('must contain a JSON object')) {\n    console.error(`Unwrap arrays/scalars in ${queryFile}: top level must be {\"table\":..., \"select\":...}`);\n  } else throw e;\n}","preventionTips":["Validate query files with `jq type file.json` — expect \"object\"","Store one query per file; keep result dumps in separate files from queries","When generating query files programmatically, assert the serialized top level is an object"],"tags":["cli","json","input-validation","aql"],"backgroundTag":"invalid-json-input","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}