{"record":{"id":"8e7b35c5eb15e94d","repo":"windmill-labs/windmill","slug":"completed-jobs-file-must-contain-an-array-of-jobs","errorCode":null,"errorMessage":"Completed jobs file must contain an array of jobs","messagePattern":"Completed jobs file must contain an array of jobs","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"cli/src/commands/jobs/jobs.ts","lineNumber":198,"sourceCode":"            \"Import cancelled. Please scale down workers and try again.\"\n          );\n          log.info(\"You can skip this check with --skip-worker-check flag.\");\n          return;\n        }\n      }\n    } catch (e) {\n      log.debug(`Could not check for active workers: ${e}`);\n    }\n  }\n\n  // Push completed jobs\n  const completedPath = opts.completedFile || \"completed_jobs.json\";\n  try {\n    const completedContent = await readTextFile(completedPath);\n    const completedJobs = JSON.parse(completedContent);\n\n    if (!Array.isArray(completedJobs)) {\n      throw new Error(\"Completed jobs file must contain an array of jobs\");\n    }\n\n    const completedResult = await wmill.importCompletedJobs({\n      workspace: ws.workspaceId,\n      requestBody: completedJobs,\n    });\n\n    log.info(colors.green(`Completed jobs: ${completedResult}`));\n  } catch (e: any) {\n    if (e.code === \"ENOENT\") {\n      log.info(\n        colors.yellow(\n          `No completed jobs file found at ${completedPath}, skipping`\n        )\n      );\n    } else {\n      throw new Error(`Failed to push completed jobs: ${e}`);\n    }","sourceCodeStart":180,"sourceCodeEnd":216,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/cli/src/commands/jobs/jobs.ts#L180-L216","documentation":"'wmill jobs push' reads the completed-jobs JSON file (default completed_jobs.json) to import via the API. The CLI requires it to be a JSON array of job objects and throws this error when the parsed top-level value is anything else.","triggerScenarios":"The completed jobs file contains a JSON object (e.g. {\"jobs\": [...]}) instead of a bare array, is a JSON-export wrapped in other structure, or the wrong file was pointed at via --completed-file.","commonSituations":"Exporting from another tool that wraps arrays in an object; hand-written file missing brackets; mixing up queued/completed file paths; truncated file parsed as something unexpected.","solutions":["Wrap the top level as an array: the file must start with [ and contain job objects","Check you passed the right file to --completed-file","Validate with a JSON parser (jq 'type') that the root is an array","Re-export from the source ensuring a bare array of completed-job objects"],"exampleFix":"// before (completed_jobs.json)\n{\"jobs\": [{\"path\": \"f/foo\", ...}]}\n// after\n[{\"path\": \"f/foo\", ...}]","handlingStrategy":"validation","validationCode":"import { readTextFile } from \"node:fs/promises\"; // or Deno\nconst jobs = JSON.parse(await readTextFile(completedPath));\nif (!Array.isArray(jobs)) {\n  throw new Error(`${completedPath} must be a JSON array of job objects`);\n}","typeGuard":"function isJobArray(v: unknown): v is Record<string, unknown>[] {\n  return Array.isArray(v) && v.every((j) => j !== null && typeof j === \"object\");\n}","tryCatchPattern":"try {\n  await wmillJobsPush({ completedFile: path });\n} catch (e) {\n  if (String(e).includes(\"must contain an array of jobs\")) {\n    console.error(`Fix ${path}: top-level must be [ ... ], not an object`);\n  }\n}","preventionTips":["Export completed jobs as a bare JSON array, not wrapped in an object","Validate the file with 'jq type' (expect 'array') before pushing","Don't confuse completed_jobs.json with queued_jobs.json paths","Add a CI lint step validating the schema of these files"],"tags":["cli","json","validation","jobs"],"backgroundTag":"schema-validation-failed","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}