Portal Community
Why this guide exists Several real bugs this year turned out to trace back to a misunderstanding of exactly these mechanics — a self-nesting pinned-data cycle, output modes defaulting wrong, and lifecycle events carrying more (or less) than intended. Every claim on these pages was verified by reading the actual executing code, with file and line references, so this can serve as the one place engineers check before changing anything in this area.

The Five Pieces

The Shape, at a Glance

NodeExecutionResult
├── OutputData          Dictionary<string, object>   ← the executor's own output
├── OutputBranches       INodeResultBranches           ← SIBLING, not nested inside OutputData
│   ├── Success[]  → INodeDataItem { Data }             (Data is a COPY of OutputData's fields)
│   ├── Error[]
│   └── Conditional{}
├── OutputPortKey        string   e.g. "main", "error"
├── IsSuccess / IsSkipped / IsRetry
├── DurationMs
└── ErrorMessage / Exception

OutputData and OutputBranches are two separate properties on the same object — never one nested inside the other. Whenever you see a shape that looks self-nested (an OutputData field containing another OutputBranches), that's a sign something serialized a whole NodeExecutionResult and mistakenly treated it as one flat data record. See Pinned Data for the real bug this caused.

Read in This Order

1

Input Data → Output Data

Start with how a node receives data and how its own output gets seeded before its logic even runs.

2

Node Execution Result → Output Branches

Then see how that Output Data gets wrapped for the rest of the engine to consume.

3

Execution Memory → Lifecycle Stages

See how one node's result becomes the next node's input, and the exact stages a node passes through to get there.

4

Metadata & Events → Pinned Data

Finish with what actually gets published to the UI/logs today, and a real bug that ties every piece above together.