Skip to main content

Overview

Solve common problems with Conductor workflows. This guide covers debugging techniques, common errors, performance issues, and integration challenges.

Debugging Techniques

Enable Detailed Logging

name: debug-workflow
description: Workflow with detailed logging

flow:
  - member: log-input
    type: Function
    input:
      level: "debug"
      message: "Input received"
      data: ${JSON.stringify(input)}

  - member: process-data
    type: Function

  - member: log-output
    type: Function
    input:
      level: "debug"
      message: "Processing complete"
      data: ${JSON.stringify(process-data.output)}

output:
  result: ${process-data.output}

Trace Execution Flow

// Worker with execution tracing
import { Conductor } from '@ensemble-edge/conductor';

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const traceId = crypto.randomUUID();
    console.log(`[${traceId}] Starting execution`);

    try {
      const conductor = new Conductor({
        env,
        onMemberStart: (member) => {
          console.log(`[${traceId}] Starting: ${member.name}`);
        },
        onMemberComplete: (member, result) => {
          console.log(`[${traceId}] Completed: ${member.name} (${result.duration}ms)`);
        }
      });

      const result = await conductor.executeEnsemble(
        'my-workflow',
        await request.json()
      );

      console.log(`[${traceId}] Execution complete`);
      return Response.json(result);
    } catch (error) {
      console.error(`[${traceId}] Error:`, error);
      return Response.json({ error: error.message }, { status: 500 });
    }
  }
};

Inspect State Changes

flow:
  - member: log-initial-state
    type: Function
    state:
      use: all
    input:
      state: ${JSON.stringify(state)}

  - member: update-state
    type: Function
    state:
      set: [userData, processedAt]

  - member: log-updated-state
    type: Function
    state:
      use: all
    input:
      state: ${JSON.stringify(state)}

Common Errors

1. “Ensemble not found”

Error: Error: Ensemble 'my-workflow' not found Cause: Ensemble file not in correct location or not deployed Solution:
# Check ensemble exists
ls ensembles/my-workflow.yaml

# Verify wrangler.toml configuration
cat wrangler.toml | grep ensembles

# Deploy with ensembles
npx wrangler deploy
Correct Structure:
project/
├── ensembles/
│   └── my-workflow.yaml
├── src/
│   └── index.ts
└── wrangler.toml

2. “Member execution failed”

Error: Error: Member 'fetch-data' failed: Connection timeout Cause: Network issue, API down, or timeout too short Solution:
# Add timeout and retry
- member: fetch-data
  type: Fetch
  config:
    url: "${env.API_URL}"
    timeout: 30000  # Increase timeout
    retries: 3      # Add retries
  continue_on_error: true  # Continue on failure

# Handle error
- member: handle-error
  condition: ${!fetch-data.success}
  type: Function
  input:
    error: ${fetch-data.error}

3. “Invalid JSONata expression”

Error: Error: JSONata evaluation failed: Unexpected token Cause: Syntax error in JSONata expression Solution:
# ❌ Invalid - missing quotes
expression: $filter(items, status = active)

# ✅ Valid - quoted string
expression: $filter(items, status = "active")

# Test expressions separately
- member: test-transform
  type: Transform
  input:
    data: { "test": true }
    expression: "test"  # Simple test first

4. “State property not found”

Error: Error: State property 'userData' not set Cause: Trying to use state before it’s set Solution:
flow:
  # Set state first
  - member: load-user
    type: Data
    state:
      set: [userData]  # Set state

  # Then use it
  - member: process-user
    condition: ${state.userData != null}  # Check exists
    state:
      use: [userData]  # Use state

5. “AI Provider rate limit”

Error: Error: Rate limit exceeded (429) Cause: Too many requests to AI provider Solution:
# Add exponential backoff
- member: ai-task
  type: Think
  retry:
    maxAttempts: 5
    backoff: exponential
    initialDelay: 1000

# Use AI Gateway caching
config:
  provider: openai
  routing: cloudflare-gateway  # Automatic caching

# Batch requests
- member: batch-classify
  type: Think
  input:
    prompt: |
      Classify each item:
      ${items.map((item, i) => `${i+1}. ${item}`).join('\n')}

6. “Database query timeout”

Error: Error: D1 query timeout after 30s Cause: Slow query or missing index Solution:
-- Add index for common queries
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_orders_user_id ON orders(user_id);

-- Optimize query
-- ❌ Slow - no index
SELECT * FROM orders WHERE created_at > '2024-01-01';

-- ✅ Fast - indexed
SELECT * FROM orders WHERE user_id = ? ORDER BY created_at DESC LIMIT 10;
# Add caching
- member: fetch-user
  type: Data
  cache:
    ttl: 300  # Cache 5 minutes
  config:
    storage: d1
    operation: query
    query: "SELECT * FROM users WHERE id = ?"

7. “KV value too large”

Error: Error: KV value exceeds 25MB limit Cause: Trying to store large object in KV Solution:
# Split large data
- member: store-metadata
  type: Data
  config:
    storage: kv
    operation: put
  input:
    key: "data:${input.id}:metadata"
    value: ${metadata}  # Small metadata in KV

- member: store-content
  type: Data
  config:
    storage: r2  # Use R2 for large files
    operation: put
  input:
    key: "data:${input.id}:content"
    value: ${largeContent}

8. “HITL timeout”

Error: Error: HITL member 'review' timed out after 1 hour Cause: Human didn’t respond in time Solution:
- member: request-review
  type: HITL
  config:
    timeout: 86400000  # Increase to 24 hours
    onTimeout: continue  # Or continue on timeout

# Handle timeout
- member: auto-approve-on-timeout
  condition: ${request-review.timedOut}
  type: Function
  input:
    reason: "Auto-approved due to timeout"

Performance Issues

Slow Execution

Symptom: Workflow takes too long to complete Diagnosis:
# Add timing
output:
  metrics:
    member1Duration: ${member1.duration}
    member2Duration: ${member2.duration}
    totalDuration: ${execution.duration}
Solutions:
  1. Enable Parallel Execution:
# ❌ Sequential - 300ms
flow:
  - member: task1  # 100ms
  - member: task2  # 100ms
  - member: task3  # 100ms

# ✅ Parallel - 100ms
flow:
  parallel:
    - member: task1
    - member: task2
    - member: task3
  1. Add Caching:
- member: expensive-operation
  cache:
    ttl: 3600
  1. Use Faster Models:
# ❌ Slow - gpt-4o (~2s)
config:
  model: gpt-4o

# ✅ Fast - gpt-4o-mini (~200ms)
config:
  model: gpt-4o-mini

High Costs

Symptom: AI bills higher than expected Diagnosis:
# Track token usage
- member: log-usage
  type: Data
  config:
    storage: d1
    operation: query
    query: |
      INSERT INTO usage_log (member, tokens, cost)
      VALUES (?, ?, ?)
  input:
    params:
      - "expensive-ai-call"
      - ${expensive-ai-call.output.usage.total_tokens}
      - ${expensive-ai-call.output.usage.total_tokens * 0.00001}
Solutions:
  1. Cache AI Responses:
config:
  provider: openai
  routing: cloudflare-gateway  # Persistent cache
  1. Reduce Token Usage:
# ❌ Verbose prompt (100 tokens)
prompt: |
  I would like you to carefully analyze the following text
  and determine whether the sentiment is positive or negative.
  Please be thorough in your analysis.

  Text: ${text}

# ✅ Concise prompt (10 tokens)
prompt: "Classify sentiment (positive/negative): ${text}"
  1. Use Cheaper Models:
# Simple tasks
config:
  model: gpt-4o-mini  # $0.15/$0.60 per 1M tokens

# Complex tasks only
config:
  model: gpt-4o  # $5.00/$15.00 per 1M tokens

Integration Issues

AI Provider Authentication

Error: Error: Invalid API key Solution:
# Set secret
echo "sk-..." | npx wrangler secret put OPENAI_API_KEY

# Verify in wrangler.toml
cat wrangler.toml
# Use in workflow
config:
  provider: openai
  apiKey: ${env.OPENAI_API_KEY}

Webhook Signature Verification Failed

Error: Error: Invalid webhook signature Solution:
// Verify signature
import crypto from 'crypto';

async function verifySignature(
  request: Request,
  secret: string
): Promise<boolean> {
  const signature = request.headers.get('X-Signature');
  const body = await request.clone().text();

  const hmac = crypto.createHmac('sha256', secret);
  hmac.update(body);
  const expected = hmac.digest('hex');

  return signature === expected;
}

CORS Issues

Error: Access-Control-Allow-Origin header is missing Solution:
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    // Handle preflight
    if (request.method === 'OPTIONS') {
      return new Response(null, {
        headers: {
          'Access-Control-Allow-Origin': '*',
          'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
          'Access-Control-Allow-Headers': 'Content-Type',
          'Access-Control-Max-Age': '86400'
        }
      });
    }

    // Handle request
    const response = await handleRequest(request, env);

    // Add CORS headers
    response.headers.set('Access-Control-Allow-Origin', '*');
    return response;
  }
};

Testing Issues

Mock Data Not Working

Problem: Mocks not being used in tests Solution:
// Ensure mocks are properly configured
const conductor = await TestConductor.create({
  mocks: {
    ai: {
      responses: {
        'member-name': { result: 'mocked response' }
      }
    },
    db: {
      users: [
        { id: 1, name: 'Test User' }
      ]
    }
  }
});

Async Assertion Failures

Problem: Tests fail with timing issues Solution:
// ❌ Not waiting for completion
const result = conductor.executeEnsemble('workflow', input);
expect(result.output).toBeDefined();

// ✅ Await completion
const result = await conductor.executeEnsemble('workflow', input);
expect(result.output).toBeDefined();

// ✅ Wait for HITL
const execution = await conductor.executeEnsemble('workflow', input);
expect(execution.status).toBe('waiting_for_input');

await conductor.respondToHITL(execution.id, { approved: true });
const completed = await conductor.waitForCompletion(execution.id);
expect(completed.output.approved).toBe(true);

Deployment Issues

Wrangler Deploy Fails

Error: Error: Failed to publish your Function Checklist:
# 1. Verify authentication
npx wrangler whoami

# 2. Check wrangler.toml syntax
npx wrangler deploy --dry-run

# 3. Verify bindings
cat wrangler.toml | grep -A 5 "binding"

# 4. Check compatibility date
# wrangler.toml
compatibility_date = "2024-01-01"  # Not too old

# 5. Deploy with verbose logging
npx wrangler deploy --verbose

Environment Variables Not Set

Error: Error: env.OPENAI_API_KEY is undefined Solution:
# Local development - .dev.vars
OPENAI_API_KEY=sk-...

# Production - secrets
echo "sk-..." | npx wrangler secret put OPENAI_API_KEY --env production

# Verify secrets exist
npx wrangler secret list --env production

Getting Help

Enable Debug Mode

const conductor = new Conductor({
  env,
  debug: true  // Verbose logging
});

Collect Diagnostic Info

console.log(JSON.stringify({
  version: '1.0.0',
  ensemble: 'my-workflow',
  input: input,
  error: error.message,
  stack: error.stack,
  timestamp: Date.now()
}));

Check Documentation

Community Support

  • GitHub Discussions
  • Discord Community
  • Stack Overflow (tag: conductor)

Best Practices

  1. Always add error handling - Use continue_on_error and handle failures
  2. Log comprehensively - Structured JSON logs for debugging
  3. Test locally first - Use wrangler dev before deploying
  4. Monitor production - Set up alerts for errors
  5. Version ensembles - Use Edgit for version control
  6. Cache aggressively - Reduce costs and improve performance
  7. Use typed inputs - Validate input schemas
  8. Document workflows - Add descriptions to ensembles
  9. Review logs regularly - Catch issues early
  10. Keep dependencies updated - Security and bug fixes