Portal Community

GetNodeOutput — Access by nodeId

// Reading typed output from a specific upstream node
public override async Task<NodeExecutionResult> ExecuteAsync(
    NodeExecutionContext ctx,
    CancellationToken ct)
{
    // Read from a node named "parse-json" in the workflow
    var parsed = ctx.ExecutionMemory.GetNodeOutput<ParsedDocument>("parse-json");

    if (parsed is null)
    {
        ctx.Observability.Logger.LogWarning("parse-json node output is null — node may not have run");
        return NodeExecutionResult.Failure("Required upstream data not available");
    }

    // Use the parsed document
    var summary = await _summarizer.SummarizeAsync(parsed.Content, ct);
    return NodeExecutionResult.Success(summary);
}

GetPreviousNodeOutput — Immediate Upstream Shortcut

GetPreviousNodeOutput<T>() returns the output of the node that connects directly into this node's input port. It is equivalent to calling GetNodeOutput<T>(previousNodeId), but the caller does not need to know the upstream node's ID — the engine resolves it from the graph topology.

// The previous node shortcut — most useful for linear pipelines
public override async Task<NodeExecutionResult> ExecuteAsync(
    NodeExecutionContext ctx,
    CancellationToken ct)
{
    // Reads output from whichever node's output port connects into this node's input port
    var input = ctx.ExecutionMemory.GetPreviousNodeOutput<string>();

    if (input is null)
        return NodeExecutionResult.Failure("No input received");

    return NodeExecutionResult.Success(input.ToUpperInvariant());
}

GetPreviousNodeOutput — When to Use Each

MethodUse WhenRisk
GetNodeOutput<T>(nodeId)Node reads from a non-adjacent upstream node, or when node identity mattersBreaks silently if nodeId changes in the workflow
GetPreviousNodeOutput<T>()Node always reads from its immediate upstream predecessor (linear pipeline)Ambiguous if node has multiple incoming edges — engine picks the primary port

Multiple Inputs — Fan-In Pattern

When a node has multiple incoming edges (fan-in), use named GetNodeOutput calls to read each upstream node independently. GetPreviousNodeOutput only reads one — the one connected to the primary input port.

// MergeNode.cs — reads from two named upstream nodes
public override async Task<NodeExecutionResult> ExecuteAsync(
    NodeExecutionContext ctx,
    CancellationToken ct)
{
    var leftResult  = ctx.ExecutionMemory.GetNodeOutput<DataSet>("left-branch-node");
    var rightResult = ctx.ExecutionMemory.GetNodeOutput<DataSet>("right-branch-node");

    var merged = Merge(leftResult, rightResult);
    return NodeExecutionResult.Success(merged);
}

Null Safety Patterns

// Pattern 1 — fail fast on missing required input
var input = ctx.ExecutionMemory.GetNodeOutput<Order>("order-fetch");
if (input is null) return NodeExecutionResult.Failure("order-fetch output missing");

// Pattern 2 — fallback to default if input is optional
var config = ctx.ExecutionMemory.GetNodeOutput<ProcessConfig>("config-node")
    ?? ProcessConfig.Default;

// Pattern 3 — check and log
var data = ctx.ExecutionMemory.GetNodeOutput<ApiResult>("api-call-node");
if (data is null)
{
    ctx.Observability.Logger.LogWarning("api-call-node output missing — using empty result");
    data = ApiResult.Empty;
}
GetNodeOutput always returns null for nodes that have not yet run. The execution engine runs nodes in topological order. If you call GetNodeOutput("node-b") from within node-a, and node-b is downstream of node-a, the result is always null — node-b cannot have run before node-a in a standard graph without cycles.