Skip to content

Node Results

Result types returned by node executors during workflow execution.

Overview

Node executors return NodeResult to indicate execution outcome and control flow. The engine uses these results to determine the next steps in workflow execution.

NodeResult Hierarchy

NodeResult hierarchyA sealed NodeResult root fans out to continuation, wait, completion, failure, join, subflow, async subflow, and timer result types. WaitForSignalResult has user-task and service-task wait subtypes.NodeResultsealedContinueResultMove to target node(s)WaitForSignalResultPause for external signalCompleteWorkflowResultWorkflow finishedFailWorkflowResultWorkflow failedWaitForJoinResultWait for branchesWaitForSubflowResultStart and wait for childAsyncSubflowResultStart child asyncWaitForTimerResultWait for timerWaitForUserTaskResultCreate user taskWaitForServiceTaskResultService task routing

ContinueResult

Class Signature

Properties

PropertyTypeDefaultDescription
targetNodeIdsList<String>--Target node IDs to continue to
outputMap<String, dynamic>const {}Data to merge into workflow output
effectsList<WorkflowEffect>const []Side effects to process before continuing

Indicates successful execution with continuation to target node(s).

Usage Examples

Single target:

dart
return ContinueResult.single('processData',
  output: {'validated': true},
);

Multiple targets (parallel execution):

dart
return ContinueResult.all(['sendEmail', 'sendSms'],
  output: {'notificationStarted': true},
);

With effects:

dart
return ContinueResult.single('nextNode',
  output: {'processed': true},
  effects: [
    const CancelUserTasksEffect(),
    RecordEventEffect(event: WorkflowEvent.custom(...)),
  ],
);

WaitForSignalResult

Class Signature

Properties

PropertyTypeDefaultDescription
signalNameString--Name of signal to wait for
timeoutDuration?--Optional timeout duration

Pauses workflow execution until an external signal is received.

Usage Examples

Wait for external event:

dart
return WaitForSignalResult(
  signalName: 'payment_confirmation',
);

Wait with timeout:

dart
return WaitForSignalResult(
  signalName: 'payment_confirmation',
  timeout: Duration(hours: 1),
);

WaitForUserTaskResult

Class Signature

Properties

PropertyTypeDefaultDescription
signalNameString--Signal to wait for (inherited)
timeoutDuration?--Optional timeout (inherited)
configUserTaskConfiguration--User task configuration
effectsList<WorkflowEffect>const []Effects to apply before creating the user task

Creates a user task and waits for completion. Extends WaitForSignalResult.

UserTaskConfiguration

Class Signature

Usage Example

dart
return WaitForUserTaskResult(
  signalName: 'approval_decision',
  config: UserTaskConfiguration(
    title: 'Review Expense Report',
    schemaType: 'approval',
    assignedToRoleId: 'managers',
    priority: UserTaskPriority.high,
    input: {'expenseId': expenseId},
  ),
);

With Effects (Cancel Previous Tasks)

dart
return WaitForUserTaskResult(
  signalName: 'revision_request',
  config: UserTaskConfiguration(
    title: 'Revise Document',
    schemaType: 'revision',
    assignedToUserId: submitterId,
  ),
  // Cancel any pending approval tasks before creating revision task
  effects: [
    const CancelUserTasksEffect(),
  ],
);

CompleteWorkflowResult

Class Signature

Properties

PropertyTypeDefaultDescription
outputMap<String, dynamic>const {}Final workflow output

Indicates workflow has completed successfully.

Usage Example

dart
return CompleteWorkflowResult(
  output: {
    'result': 'approved',
    'completedAt': DateTime.now().toIso8601String(),
  },
);

FailWorkflowResult

Class Signature

Properties

PropertyTypeDefaultDescription
errorTypeErrorType--Error category (enum)
messageString--Human-readable message
isRetryableboolfalseWhether retry might succeed
detailsMap<String, dynamic>?--Additional error context

Indicates workflow failed with an error.

ErrorType Enum

dart
enum ErrorType {
  validation,  // Invalid input/configuration
  timeout,     // Operation timed out
  activity,    // Activity execution failed
  condition,   // Gateway condition evaluation failed
  internal,    // Internal engine error
  cancelled,   // Operation was cancelled
}

Usage Examples

Validation error:

dart
return FailWorkflowResult.validation(
  'Required field missing: entityId',
  details: {'field': 'entityId'},
);

Timeout error (retryable):

dart
return FailWorkflowResult.timeout(
  'Payment gateway timeout',
  details: {'gateway': 'stripe', 'attemptNumber': 2},
);

Custom error:

dart
return FailWorkflowResult.custom(
  errorType: ErrorType.activity,
  message: 'Task execution failed',
  isRetryable: true,
  details: {'taskId': taskId},
);

TaskResult

Result types returned specifically by task executors.

TaskResult Hierarchy

TaskResult hierarchyA sealed TaskResult root has two outcomes: TaskSuccess for completed work and TaskFailure for failed work.TaskResultsealedTaskSuccessTask completed successfullyTaskFailureTask failed

TaskSuccess

Class Signature

Properties

PropertyTypeDefaultDescription
effectsList<WorkflowEffect>--Side effects to apply after task completion
outputMap<String, dynamic>--Computed: merged output from all SetOutputEffects
outputPortIdString?--Computed: port ID from RouteToPortEffect (if any)

Usage

dart
Future<TaskResult> execute(ExecutionContext context) async {
  final result = await processData(context.input);

  return TaskSuccess([SetOutputEffect(output: {
    'processedItems': result.items.length,
    'totalValue': result.totalValue,
    'processedAt': DateTime.now().toIso8601String(),
  })]);
}

With Effects

dart
Future<TaskResult> execute(ExecutionContext context) async {
  final result = await processData(context.input);

  return TaskSuccess([
    SetOutputEffect(output: {'processed': true}),
    // Cancel pending user tasks
    const CancelUserTasksEffect(),
    // Record audit event
    RecordEventEffect(
      event: WorkflowEvent.custom(
        instanceId: context.workflowInstanceId,
        nodeId: context.nodeId,
        eventType: 'data_processed',
        data: {'itemCount': result.items.length},
      ),
    ),
  ]);
}

TaskFailure

Class Signature

ErrorType Enum

dart
enum ErrorType {
  validation,  // Invalid input/configuration
  timeout,     // Operation timed out
  activity,    // Activity execution failed
  condition,   // Gateway condition evaluation failed
  internal,    // Internal engine error
  cancelled,   // Operation was cancelled
}

Usage

dart
Future<TaskResult> execute(ExecutionContext context) async {
  // Use get<T> for previous node output
  final userId = context.get<String>('userId');
  if (userId == null) {
    return TaskFailure(
      errorType: ErrorType.validation,
      message: 'userId is required',
    );
  }

  try {
    final user = await userService.findById(userId);
    if (user == null) {
      return TaskFailure(
        errorType: ErrorType.validation,
        message: 'User not found: $userId',
      );
    }

    return TaskSuccess([SetOutputEffect(output: {'user': user.toJson()})]);
  } on TimeoutException {
    return TaskFailure(
      errorType: ErrorType.timeout,
      message: 'User service timeout',
      isRetryable: true,
    );
  } catch (e, st) {
    return TaskFailure(
      errorType: ErrorType.internal,
      message: 'Unexpected error: $e',
      details: {'stackTrace': st.toString()},
    );
  }
}

Conversion

Task executors return TaskResult, which the engine converts to NodeResult:

dart
// Conceptual - internal engine logic
if (taskResult is TaskSuccess) {
  // Success continues to next node(s) via edges
  return ContinueResult.single(nextNodeId, output: taskResult.output);
}

if (taskResult is TaskFailure) {
  // Failure fails the workflow
  return FailWorkflowResult(
    errorType: taskResult.errorType,
    message: taskResult.message,
    details: taskResult.details,
    isRetryable: taskResult.isRetryable,
  );
}

Best Practices

1. Return Specific Errors

dart
// Good: Specific error type
return TaskFailure(
  errorType: ErrorType.validation,
  message: 'Amount must be positive',
  code: 'INVALID_AMOUNT',
);

// Avoid: Generic error
return TaskFailure(
  errorType: ErrorType.internal,
  message: 'Error occurred',
);

2. Include Context

dart
return TaskFailure(
  errorType: ErrorType.activity,
  message: 'API call failed',
  details: {
    'endpoint': '/api/orders',
    'statusCode': response.statusCode,
    'responseBody': response.body,
  },
);

3. Mark Retryable Appropriately

dart
// Retryable: Transient failures
return TaskFailure(
  errorType: ErrorType.timeout,
  isRetryable: true,
);

// Not retryable: Permanent failures
return TaskFailure(
  errorType: ErrorType.validation,
  isRetryable: false,
);

4. Return Focused Output

dart
// Good: Only necessary data
return TaskSuccess([SetOutputEffect(output: {
  'orderId': order.id,
  'status': order.status,
})]);

// Avoid: Dumping entire objects
return TaskSuccess([SetOutputEffect(output: order.toJson())]); // 50+ fields

WorkflowEffect

Effects allow executors to declaratively request side effects without directly calling mutation methods. This keeps executors pure and testable.

Common Effects

EffectDescription
SetOutputEffectSet or merge output data
CancelUserTasksEffectCancel pending user tasks
RecordEventEffectRecord workflow event
UpdateStatusEffectUpdate workflow status
CreateTokensEffectCreate new tokens for nodes

See Workflow Effects for the complete reference.

See Also