Portal Community

The Standard Node Data Envelope

Every node's input and output — CollectionOperation included — travels inside the same standard envelope. Run metadata sits at the top level; the real payload lives under items, each entry wrapping its record in "json":

{
  "timestamp": "2026-07-24T01:02:27.8652562Z",
  "triggerTime": "2026-07-24T01:02:27.8652562Z",
  "resource": "trigger",
  "operation": "manual-trigger",
  "status": "success",
  "portName": "main",
  "items": [
    {
      "json": {
        "timestamp": "2026-07-24T01:02:27.8652562Z",
        "triggerTime": "2026-07-24T01:02:27.8652562Z",
        "resource": "trigger",
        "operation": "manual-trigger",
        "status": "success",
        "portName": "main"
      }
    }
  ],
  "success": true
}

CollectionOperation resolves the collection to operate on from InputData["items"] by default, unwrapping each entry's "json" value into one element — the same shared ResolveSourceRecords/ExtractRecordsFromItems helper DataMapping uses.

Inline test data does NOT need the envelope shape

When you type a JSON array directly into inlineDataSource to test an operation, you write a plain array — [{...}, {...}] — not {"items":[{"json":{...}}]}. The node parses whatever you typed and normalizes it into the standard items/json shape before producing output. The envelope is the wire format between nodes; it is not what you author by hand.

When to Use

Configuration

SettingRequiredDescription
operation Required The operation to perform on the collection. One of: filter, map, reduce, sort, distinct, count, first, last, reverse.
inlineDataSource Optional An inline JSON array literal. Shared base-class setting (also used by DataMapping) — not unique to this node. If non-empty, it wins outright over reading InputData["items"].
expression Optional* A JavaScript expression evaluated per item (for filter, map) or per accumulation step (for reduce). The current element is item; reduce additionally exposes acc. *Required for filter, map, and reduce — validation fails without it.
field Optional Field name used by sort (the field to sort by) and distinct (the field to deduplicate on). Omit for arrays of primitives.
direction Optional Sort direction for the sort operation. "asc" (default) or "desc".
initialValue Optional The starting accumulator value for the reduce operation, given as a plain string that's parsed into a number, boolean, or left as a string (e.g. "0" for sum, "" for concatenation). Defaults to null if omitted.
onError Optional Shared base-class setting (also on DataMapping). "continue" (default) skips failing items and never fails the node on error count alone. "error" fails the node once errorCount exceeds maxErrorCount. See Error Handling below.
maxErrorCount Optional Default 0. Only consulted when onError is "error": the node fails once errorCount exceeds this value. 0 means "fail on any error."
maxErrorsToCollect Optional Default 0 (unlimited). Safety cap: once this many item errors have occurred, the operation stops processing further items and the node fails — regardless of onError. Protects against a systemically-broken expression burning through a huge collection before anyone notices.

Operations Reference

OperationExpression VariablesResult ShapeDescription
filteritem — current elementList (subset), via itemsEvaluates the expression for each element. Keeps only elements where the expression is truthy. item.status === "active" keeps only active items.
mapitem — current elementList (transformed), via itemsTransforms each element by evaluating the expression. item.id returns an array of ID strings. { id: item.id, label: item.name } returns reshaped objects. A result of NaN (e.g. arithmetic on a missing/non-numeric field) is treated as a per-item error, not silently written through.
reduceacc — accumulator, item — current elementScalar or object, via a single output itemFolds the collection to a single value. acc + item.total with initialValue: "0" produces a sum. A NaN result at any step is a per-item error (that step is skipped; acc keeps its prior value).
sortn/a — uses field and directionList (sorted), via itemsSorts by the specified field in ascending or descending order. Uses natural comparison for strings and numeric comparison for numbers.
distinctn/a — uses fieldList (deduplicated), via itemsReturns unique elements, keeping the first occurrence. For object arrays, specify field to deduplicate by field value. For primitive arrays, omit field.
countn/aInteger, via a single output itemReturns the total number of elements in the resolved collection.
firstn/aElement or null, via a single output itemReturns the first element, or null if the collection is empty.
lastn/aElement or null, via a single output itemReturns the last element, or null if the collection is empty.
reversen/aList (reversed), via itemsReturns the collection in reverse order. Does not sort — simply reverses the existing element order.

Error Handling: Per-Item Failures Don't Abort the Batch

filter, map, and reduce evaluate a JS expression once per item. A single bad item — a thrown expression error, or a NaN arithmetic result — never aborts the whole operation. It's recorded as a failure in errorRecords/errorCount, and processing continues with the next item.

SettingEffect
onError: "continue" (default)Failing items are skipped and counted. The node always succeeds regardless of how many items failed (unless maxErrorsToCollect triggers a circuit-break — see below).
onError: "error"The node fails (routes to error) once errorCount exceeds maxErrorCount. With the default maxErrorCount: 0, that means any single item failure fails the node.
maxErrorsToCollect > 0A safety cap that applies regardless of onError: once this many failures have occurred, the operation stops processing further items immediately and the node fails, reporting "...stopped after N item error(s) reached maxErrorsToCollect".

Even in onError: "continue" mode, the failed items are not silently dropped from the record — each one appears in errorRecords as {"item": <original item>, "error": "<message>"}, capped at maxErrorsToCollect entries (or unlimited if it's 0) even though errorCount keeps counting every failure beyond that cap.

Output Ports

PortWhen It Fires
successThe operation completed. This includes the case where some items failed under onError: "continue" — check errorCount to detect partial failures even on the success port.
errorThe collection could not be resolved at all (no inlineDataSource and no upstream items), the operation name is missing/invalid, the operation-specific expression is missing, or the error-handling thresholds above (onError: "error" + maxErrorCount, or maxErrorsToCollect) were exceeded.

Output Fields

FieldTypeDescription
statusstringLiteral "success" on the success path.
successCountintegerNumber of items that processed without error.
errorCountintegerTotal number of items that failed, including any beyond the maxErrorsToCollect cap.
errorRecordsarrayUp to maxErrorsToCollect failed items, each as {"item": <original>, "error": "<message>"}.
countintegerOnly present for list-shaped operations (filter/map/sort/distinct/reverse) — the number of successful result records, i.e. successCount.
itemsarrayStandard [{ "json": <record> }, ...] shape. For list operations, one item per successful result record. For scalar operations (reduce/count/first/last), a single item wrapping the scalar or element value.

Sample Configurations

Filter — only active orders, tolerate bad records

{
  "nodeType": "collection-operation",
  "settings": {
    "operation": "filter",
    "expression": "item.status === \"active\"",
    "onError": "continue",
    "maxErrorsToCollect": 10
  }
}

Map — extract order IDs only

{
  "nodeType": "collection-operation",
  "settings": {
    "operation": "map",
    "expression": "item.orderId"
  }
}

Reduce — sum all line item amounts

{
  "nodeType": "collection-operation",
  "settings": {
    "operation": "reduce",
    "expression": "acc + item.amount",
    "initialValue": "0"
  }
}

Sort — newest records first

{
  "nodeType": "collection-operation",
  "settings": {
    "operation": "sort",
    "field": "createdAt",
    "direction": "desc"
  }
}

Sample Output — filter with partial errors

{
  "status": "success",
  "successCount": 2,
  "errorCount": 1,
  "errorRecords": [
    { "item": { "id": "item_003", "status": null }, "error": "Cannot read properties of null (reading 'toLowerCase')" }
  ],
  "count": 2,
  "items": [
    { "json": { "id": "item_001", "status": "active" } },
    { "json": { "id": "item_002", "status": "active" } }
  ]
}

Expression Reference

ExpressionValue
{{ $output.collectionOp.items[0].json }}The first result record (or the scalar/element for reduce/count/first/last).
{{ $output.collectionOp.successCount }}How many items processed without error — useful for downstream IfCondition branching.
{{ $output.collectionOp.errorCount }}How many items failed. Check this even on the success port to detect partial failures under onError: "continue".

Node Policies & GuardRails

PolicyRationale
Check errorCount even on the success portUnder the default onError: "continue", the node still reports success even when some items failed — the failures are visible only in errorCount/errorRecords, not by port routing.
Use item (and acc for reduce) — not the source field name — in expressionsEvery element in the collection is exposed as item regardless of the field names in the source object. Write item.status, not order.status.
Set onError: "error" only when any single bad item should hard-fail the workflowThe default "continue" is almost always right for exploratory/bulk operations. Reserve "error" + a tuned maxErrorCount for pipelines where partial success is unacceptable.
Set a maxErrorsToCollect cap on large or untrusted collectionsWithout a cap, a systemically-broken expression (e.g. referencing a field that doesn't exist on any item) still runs to completion before you find out — every item fails, one at a time. A cap stops the operation early.
Always provide an explicit initialValue for reduceWithout one, the accumulator starts as null, which can produce unexpected results (e.g. null + item.total evaluating to NaN on the first item, recorded as an error) rather than the numeric/string seed you intended.
For complex multi-step transformations, chain multiple CollectionOperation nodes or use CodeExecuteCollectionOperation is designed for single-operation array manipulation. When you need to chain multiple operations (filter then map then sort), either use multiple nodes in sequence or a single CodeExecute node.

Pattern Examples

See Examples for elaborate, realistic worked examples including error-handling behavior and inlineDataSource usage.

Pattern 1 — Filter Active Subscriptions, Then Extract Emails for Bulk Send

// Node 1: CollectionOperation — filter active premium subscriptions
{ "operation": "filter", "expression": "item.status === \"active\" && item.tier === \"premium\"" }
// items: premium active subscription records only

// Node 2: CollectionOperation — extract customer email addresses
{ "operation": "map", "expression": "item.customerEmail" }
// items: one item per email string, ready for bulk email send

Pattern 2 — Sum Invoice Totals and Get Distinct Regions

// Node 1: Sum all invoice amounts
{ "operation": "reduce", "expression": "acc + item.totalAmount", "initialValue": "0" }

// Node 2: Get distinct billing regions from the same invoices
{ "operation": "distinct", "field": "billingRegion" }
// items: ["EMEA", "APAC", "AMER"] (unique regions only), one item per region