windmill-labs/windmill · error · Error

Queued jobs file must contain an array of jobs

Error message

Queued jobs file must contain an array of jobs

What it means

Thrown by `wmill job push` (pushJobs) in the Windmill CLI when the queued-jobs file (default queued_jobs.json, overridable with --queued-file) parses as valid JSON but is not a JSON array. The CLI forwards the parsed value directly as the `requestBody` of `wmill.importQueuedJobs`, whose API contract requires a list of queued-job objects, so a non-array body is rejected before any network call.

Source

Thrown at cli/src/commands/jobs/jobs.ts:226

    if (e.code === "ENOENT") {
      log.info(
        colors.yellow(
          `No completed jobs file found at ${completedPath}, skipping`
        )
      );
    } else {
      throw new Error(`Failed to push completed jobs: ${e}`);
    }
  }

  // Push queued jobs
  const queuedPath = opts.queuedFile || "queued_jobs.json";
  try {
    const queuedContent = await readTextFile(queuedPath);
    const queuedJobs = JSON.parse(queuedContent);

    if (!Array.isArray(queuedJobs)) {
      throw new Error("Queued jobs file must contain an array of jobs");
    }

    const queuedResult = await wmill.importQueuedJobs({
      workspace: ws.workspaceId,
      requestBody: queuedJobs,
    });

    log.info(colors.green(`Queued jobs: ${queuedResult}`));
  } catch (e: any) {
    if (e.code === "ENOENT") {
      log.info(
        colors.yellow(`No queued jobs file found at ${queuedPath}, skipping`)
      );
    } else {
      throw new Error(`Failed to push queued jobs: ${e}`);
    }
  }
}

View on GitHub (pinned to e474e8803c)

Solutions

  1. Open the file and ensure the top-level value is a JSON array: wrap the object(s) in [ ... ].
  2. If the file wraps jobs in an object like {"jobs": [...]}, extract the inner array into the file.
  3. If converting from JSONL, merge the newline-delimited objects into a single comma-separated array.
  4. Re-export the queued jobs with the matching `wmill job pull` command so the file has the canonical array shape.

Example fix

// before (queued_jobs.json)
{"path": "f/my_script", "args": {}}

// after
[{"path": "f/my_script", "args": {}}]
Defensive patterns

Strategy: validation

Validate before calling

const jobs = JSON.parse(await readTextFile(queuedPath));
if (!Array.isArray(jobs)) throw new Error(`${queuedPath} must be a JSON array of jobs`);

Type guard

function isJobArray(v: unknown): v is Record<string, unknown>[] {
  return Array.isArray(v) && v.every((x) => typeof x === "object" && x !== null);
}

Try / catch

try {
  await wmill.importQueuedJobs({ workspace: ws, requestBody: queuedJobs });
} catch (e) {
  if (String(e).includes("must contain an array")) {
    console.error(`Fix ${queuedPath}: top level must be [...]`);
  }
  throw e;
}

Prevention

When it happens

Trigger: Running `wmill job push` with a queued-jobs file whose top-level JSON value is an object ({...}), a string, a number, or null instead of an array of job objects.

Common situations: The file was hand-edited and the array wrapper was lost; a single job object was exported instead of an array; the file was written by another tool that emits {"jobs": [...]} wrapper objects; a JSONL file (multiple objects, no enclosing array) was pointed at --queued-file.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/ecc210bec1e79720. Report an issue: GitHub.