Hmbown/CodeWhale · error · Error

parallel(): max items per call

Error message

parallel(): max ${MAX_ITEMS} items per call

What it means

Thrown by the prelude's `globalThis.parallel` in vm.rs when the thunks array exceeds MAX_ITEMS (the PARALLEL_MAX_ITEMS constant baked into the prelude template). The cap bounds fan-out size so replayed runs stay within predictable resource limits.

Solutions

  1. Chunk the work: split items into batches of at most MAX_ITEMS and run batches sequentially (or via pipeline).
  2. Batch inside the host task instead: send the whole collection to one `task()` call and let the host process it.
  3. Raise PARALLEL_MAX_ITEMS if your workload legitimately needs more (rebuild with a larger constant).
  4. Pre-check `thunks.length` in-script and fail with a clearer domain-specific message.

Example fix

// before
const results = await parallel(items.map(makeThunk));
// after
const results = [];
for (let i = 0; i < items.length; i += MAX) {
  results.push(...await parallel(items.slice(i, i + MAX).map(makeThunk)));
}
Defensive patterns

Strategy: validation

Validate before calling

function assertFanoutSize(thunks, MAX_ITEMS) {
  if (thunks.length > MAX_ITEMS) {
    throw new Error(`fan-out of ${thunks.length} exceeds parallel() cap of ${MAX_ITEMS}; chunk the input`);
  }
}

Try / catch

try {
  const results = await parallel(thunks);
} catch (e) {
  if (e.message.startsWith('parallel(): max ')) {
    const max = Number(e.message.match(/max (\d+)/)?.[1]);
    console.error(`chunk input into batches of ${max} or move bulk work into a single host task`);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling `parallel(thunks)` where `thunks.length > MAX_ITEMS` (the limit value is printed in the error message); building the array dynamically from a large input collection without chunking.

Common situations: Mapping a large dataset (thousands of rows) into per-item thunks; unbounded fan-out over user-supplied lists; a config change that grew batch sizes past the cap.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/fc8f7236ed5a16da. Report an issue: GitHub.

Appendix: source

Thrown at crates/workflow-js/src/vm.rs:1546

    const envelope = JSON.parse(await hostTask(JSON.stringify(opts)));
    if (envelope.error !== undefined) {
      const err = new Error(envelope.error);
      // The host always names the kind; a missing one means an envelope this
      // prelude did not produce, which is not a typed task failure.
      err.kind = HOST_KINDS.indexOf(envelope.error_kind) !== -1
        ? envelope.error_kind
        : SCRIPT_KIND;
      throw err;
    }
    return envelope.value;
  };

  globalThis.parallel = (thunks, opts) => {
    if (!Array.isArray(thunks)) {
      throw new TypeError("parallel(): expected an array of thunks");
    }
    if (thunks.length > MAX_ITEMS) {
      throw new Error("parallel(): max " + MAX_ITEMS + " items per call");
    }
    const mode = slotMode("parallel", opts);
    const failFast = mode === "fail-fast";
    const partial = mode === "partial";
    const errors = [];
    // Returns the slot value, or throws to reject the whole fan-out.
    const onSlotError = (index, err) => {
      const kind = taskErrorKind(err);
      const message = taskErrorText(err);
      stampKind(err, kind);
      // Cancellation is the run's deadline in every mode, partial included.
      if (kind === "cancelled") throw err;
      if (partial) {
        // Opt-in partial mode: every non-cancellation slot failure becomes a
        // structured value the script can branch on. It never masquerades as
        // a success -- `__taskError` is the whole point of the shape.
        errors.push({ index: index, kind: kind, message: message });
        hostLog(

View on GitHub (pinned to 73e0f67d83)