Troubleshooting

This guide helps you diagnose and resolve common issues you might encounter when using Dispatched.

Common Issues and Solutions

Authentication Errors

Invalid API Key

{
    "error": "UNAUTHORIZED",
    "data": {
        "message": "Unauthorized"
    }
}

Possible Causes:

  • API key is incorrect or malformed
  • API key has been revoked
  • Key is being used in the wrong environment

Solutions:

  1. Verify API key format: Should start with ak_
  2. Check key in dashboard: Settings > API Keys
  3. Ensure environment variables are loaded correctly:
// Check if key is loaded
if (!process.env.DISPATCHED_API_KEY) {
  throw new Error('API key not found in environment');
}

Webhook Authentication Failed

Solutions:

  1. Verify webhook secret in dashboard. Should start with wh_sec_
  2. Check authorization header format:
// Correct format
app.post('/webhook', (req, res) => {
  const authHeader = req.headers.authorization;
  if (authHeader !== `Bearer ${process.env.DISPATCHED_WEBHOOK_SECRET}`) {
    return res.status(401).json({ error: 'Unauthorized' });
  }
});

Job Processing Issues

Jobs Not Being Processed

Symptoms:

  • Jobs stay in QUEUED status
  • No webhook calls received
  • Timeouts occurring

Solutions:

Verify webhook URL is accessible

# Test your webhook endpoint
curl -X POST https://your-webhook.com/path \
  -H "Content-Type: application/json" \
  -d '{"test": true}'

Failed Jobs and Retries

Common Failure Patterns:

  1. Immediate Failures
  • Webhook returns a status other than 200 or 201
  • Payload validation errors
  • Connection timeouts
  1. Intermittent Failures
  • Network issues
  • Database connection problems
  • Rate limiting

Debugging Steps:

app.post('/webhook', async (req, res) => {
  const { jobId, attempt } = req.body;

  console.log(`Processing job ${jobId} - Attempt ${attempt}`);

  try {
    // Add timing information
    const startTime = Date.now();
    await processJob(req.body);
    const duration = Date.now() - startTime;

    console.log(`Job ${jobId} completed in ${duration}ms`);
    res.status(200).json({ success: true });
  } catch (error) {
    console.error(`Job ${jobId} failed:`, {
      error: error.message,
      stack: error.stack,
      attempt,
      timestamp: new Date().toISOString()
    });

    res.status(500).json({ error: error.message });
  }
});

Payload and Data Issues

Invalid Payload Format

{
  "error": {
    "code": "INVALID_PAYLOAD",
    "message": "Invalid payload structure"
  }
}

Solutions:

  1. Validate payload structure before sending:
const validatePayload = (payload) => {
  if (!payload || typeof payload !== 'object') {
    throw new Error('Payload must be an object');
  }

  return true;
};
  1. Check payload size limits:
const MAX_PAYLOAD_SIZE = 10 * 1024; // 10KB

if (JSON.stringify(payload).length > MAX_PAYLOAD_SIZE) {
  throw new Error('Payload size exceeds limit');
}

Schedule Format Issues

Common Errors:

  • Invalid timezone format
  • Past dates
  • Dates too far in future

Solutions:

const validateScheduleTime = (scheduleFor) => {
  const scheduledTime = new Date(scheduleFor);
  const now = new Date();
  const maxTime = new Date();
  maxTime.setFullYear(maxTime.getFullYear() + 1);

  if (isNaN(scheduledTime.getTime())) {
    throw new Error('Invalid date format');
  }

  if (scheduledTime < now) {
    throw new Error('Cannot schedule jobs in the past');
  }

  if (scheduledTime > maxTime) {
    throw new Error('Cannot schedule jobs more than 1 year in advance');
  }
};

Monitoring and Debugging

Enable Debug Logging

const DEBUG = process.env.DEBUG === 'true';

function debugLog(message, data = {}) {
  if (DEBUG) {
    console.log('[Dispatched Debug]', {
      timestamp: new Date().toISOString(),
      message,
      ...data
    });
  }
}

app.post('/webhook', (req, res) => {
  debugLog('Received webhook', {
    jobId: req.body.jobId,
    payload: req.body.payload,
    headers: req.headers
  });
  // ... process webhook
});

Track Job Processing Metrics

const metrics = {
  jobsProcessed: 0,
  jobsFailed: 0,
  totalProcessingTime: 0,

  recordSuccess(duration) {
    this.jobsProcessed++;
    this.totalProcessingTime += duration;
  },

  recordFailure() {
    this.jobsFailed++;
  },

  getStats() {
    return {
      processed: this.jobsProcessed,
      failed: this.jobsFailed,
      averageTime: this.jobsProcessed
        ? this.totalProcessingTime / this.jobsProcessed
        : 0
    };
  }
};

Getting Additional Help

Contact Support

  • Email: info@dispatched.dev
  • Include:
  • Job ID
  • Error messages
  • Relevant timestamps
  • Environment details

Next Steps