Files
OpenNoodl/dev-docs/tasks/phase-11-cloud-functions/CF11-002-error-handling-nodes
Richard Osborne ddcb9cd02e feat: Phase 5 BYOB foundation + Phase 3 GitHub integration
Phase 5 - BYOB Backend (TASK-007A/B):
- LocalSQL Adapter with full CloudStore API compatibility
- QueryBuilder translates Parse-style queries to SQL
- SchemaManager with PostgreSQL/Supabase export
- LocalBackendServer with REST endpoints
- BackendManager with IPC handlers for Electron
- In-memory fallback when better-sqlite3 unavailable

Phase 3 - GitHub Panel (GIT-004):
- Issues tab with list/detail views
- Pull Requests tab with list/detail views
- GitHub API client with OAuth support
- Repository info hook integration

Phase 3 - Editor UX Bugfixes (TASK-013):
- Legacy runtime detection banners
- Read-only enforcement for legacy projects
- Code editor modal close improvements
- Property panel stuck state fix
- Blockly node deletion and UI polish

Phase 11 - Cloud Functions Planning:
- Architecture documentation for workflow automation
- Execution history storage schema design
- Canvas overlay concept for debugging

Docs: Updated LEARNINGS.md and COMMON-ISSUES.md
2026-01-15 17:37:15 +01:00
..

CF11-002: Error Handling Nodes (Try/Catch/Retry)

Metadata

Field Value
ID CF11-002
Phase Phase 11
Series 1 - Advanced Workflow Nodes
Priority 🟡 High
Difficulty 🟡 Medium
Estimated Time 8-10 hours
Prerequisites Phase 5 TASK-007C (Workflow Runtime)
Branch feature/cf11-002-error-nodes

Objective

Create error handling nodes for workflows: Try/Catch for graceful error recovery and Retry for transient failure handling - critical for building reliable automation.

Background

External API calls fail. Databases timeout. Services go down. Production workflows need error handling:

  • Try/Catch: Wrap operations and handle failures gracefully
  • Retry: Automatically retry failed operations with configurable backoff
  • Stop/Error: Explicitly fail a workflow with a message

Without these, any external failure crashes the entire workflow.

Scope

In Scope

  • Try/Catch Node implementation
  • Retry Node implementation
  • Stop/Error Node implementation
  • Configurable retry strategies

Out of Scope

  • Global error handlers (future)
  • Error reporting/alerting (future)

Technical Approach

Try/Catch Node

const TryCatchNode = {
  name: 'Workflow Try Catch',
  displayName: 'Try / Catch',
  category: 'Workflow Logic',

  inputs: {
    run: { type: 'signal', displayName: 'Try' }
  },

  outputs: {
    try: { type: 'signal', displayName: 'Try Block' },
    catch: { type: 'signal', displayName: 'Catch Block' },
    finally: { type: 'signal', displayName: 'Finally' },
    error: { type: 'object', displayName: 'Error' },
    success: { type: 'boolean', displayName: 'Success' }
  },

  async run(context) {
    try {
      await context.triggerOutputAndWait('try');
      context.outputs.success = true;
    } catch (error) {
      context.outputs.error = { message: error.message, stack: error.stack };
      context.outputs.success = false;
      context.triggerOutput('catch');
    } finally {
      context.triggerOutput('finally');
    }
  }
};

Retry Node

const RetryNode = {
  name: 'Workflow Retry',
  displayName: 'Retry',
  category: 'Workflow Logic',

  inputs: {
    run: { type: 'signal' },
    maxAttempts: { type: 'number', default: 3 },
    delayMs: { type: 'number', default: 1000 },
    backoffMultiplier: { type: 'number', default: 2 }
  },

  outputs: {
    attempt: { type: 'signal', displayName: 'Attempt' },
    success: { type: 'signal' },
    failure: { type: 'signal' },
    attemptNumber: { type: 'number' },
    lastError: { type: 'object' }
  },

  async run(context) {
    const maxAttempts = context.inputs.maxAttempts || 3;
    const baseDelay = context.inputs.delayMs || 1000;
    const multiplier = context.inputs.backoffMultiplier || 2;

    for (let attempt = 1; attempt <= maxAttempts; attempt++) {
      context.outputs.attemptNumber = attempt;

      try {
        await context.triggerOutputAndWait('attempt');
        context.triggerOutput('success');
        return;
      } catch (error) {
        context.outputs.lastError = { message: error.message };

        if (attempt < maxAttempts) {
          const delay = baseDelay * Math.pow(multiplier, attempt - 1);
          await sleep(delay);
        }
      }
    }

    context.triggerOutput('failure');
  }
};

Stop/Error Node

const StopNode = {
  name: 'Workflow Stop',
  displayName: 'Stop / Error',
  category: 'Workflow Logic',

  inputs: {
    run: { type: 'signal' },
    errorMessage: { type: 'string', default: 'Workflow stopped' },
    isError: { type: 'boolean', default: true }
  },

  run(context) {
    const message = context.inputs.errorMessage || 'Workflow stopped';
    if (context.inputs.isError) {
      throw new WorkflowError(message);
    }
    // Non-error stop - just terminates this path
  }
};

Implementation Steps

  1. Try/Catch Node (3h) - Error boundary, output routing
  2. Retry Node (3h) - Attempt loop, backoff logic, timeout handling
  3. Stop/Error Node (1h) - Simple error throwing
  4. Testing (2h) - Unit tests, integration tests

Success Criteria

  • Try/Catch captures downstream errors
  • Retry attempts with exponential backoff
  • Stop/Error terminates workflow with message
  • Error data captured in execution history

References