Skip to main content
In some situations, you may have a parent job and need to ignore when one of its children fails. The ignoreDependencyOnFailure option allows the parent to complete without waiting for failed children, while still tracking which children failed.

How It Works

The ignoreDependencyOnFailure option makes sure that when a job fails, the dependency is ignored from the parent, so the parent will complete without waiting for the failed children. Unlike removeDependencyOnFailure, the failed children are still tracked and can be retrieved later.
As soon as a child with this option fails, the parent job will be moved to a waiting state only if there are no more pending children.

Basic Usage

import { FlowProducer } from 'bullmq';

const flow = new FlowProducer({ connection });

const originalTree = await flow.add({
  name: 'root-job',
  queueName: 'topQueueName',
  data: {},
  children: [
    {
      name: 'child-job',
      data: { idx: 0, foo: 'bar' },
      queueName: 'childrenQueueName',
      opts: { ignoreDependencyOnFailure: true },
      children: [
        {
          name: 'grandchild-1',
          data: { idx: 1, foo: 'bah' },
          queueName: 'grandChildrenQueueName',
        },
        {
          name: 'grandchild-2',
          data: { idx: 2, foo: 'baz' },
          queueName: 'grandChildrenQueueName',
        },
      ],
    },
    {
      name: 'another-child',
      data: { idx: 3, foo: 'foo' },
      queueName: 'childrenQueueName',
    },
  ],
});

Retrieving Ignored Failures

Failed children using this option can be retrieved by the getIgnoredChildrenFailures method:
const ignoredChildrenFailures =
  await originalTree.job.getIgnoredChildrenFailures();

// Returns an object with failed child keys and their error messages
// Example: { 'bull:childrenQueueName:child-job-id': 'Error message' }

Behavior

When a child job with ignoreDependencyOnFailure: true fails:
  1. The dependency is marked as “ignored” in the parent
  2. The failed child is still tracked in the parent’s dependencies
  3. The parent can complete without waiting for this child
  4. If this was the last pending child, the parent moves to waiting state
  5. You can retrieve ignored failures using getIgnoredChildrenFailures()
1

Child job fails

A child with ignoreDependencyOnFailure: true encounters an error and moves to failed state.
2

Dependency marked ignored

BullMQ marks this child as “ignored” in the parent’s dependency list.
3

Check remaining dependencies

If there are still other pending children, the parent continues waiting.
4

Parent becomes active

Once all other children complete, the parent moves to waiting state and can be processed.
5

Access ignored failures

Parent processor can retrieve information about ignored failures using getIgnoredChildrenFailures().

Complete Example

import { FlowProducer, Worker } from 'bullmq';

const flow = new FlowProducer({ connection });

// Create flow with optional children
const tree = await flow.add({
  name: 'process-order',
  queueName: 'orders',
  data: { orderId: 'ORD-123' },
  children: [
    {
      name: 'charge-payment',
      queueName: 'payments',
      data: {},
      // Required - must succeed
    },
    {
      name: 'apply-discount-code',
      queueName: 'discounts',
      data: { code: 'SAVE10' },
      opts: { ignoreDependencyOnFailure: true },
      // Optional - can fail without blocking order
    },
    {
      name: 'send-notification',
      queueName: 'notifications',
      data: { type: 'email' },
      opts: { ignoreDependencyOnFailure: true },
      // Optional - can fail without blocking order
    },
  ],
});

// Parent processor
const orderWorker = new Worker('orders', async job => {
  // Get successful children values
  const childrenValues = await job.getChildrenValues();
  
  // Get ignored failures
  const ignoredFailures = await job.getIgnoredChildrenFailures();
  
  // Log any ignored failures for monitoring
  if (Object.keys(ignoredFailures).length > 0) {
    console.warn('Some optional steps failed:', ignoredFailures);
    await logFailures(job.data.orderId, ignoredFailures);
  }
  
  return {
    orderId: job.data.orderId,
    status: 'completed',
    paymentProcessed: true,
    optionalStepsCompleted: Object.keys(childrenValues).length - 1, // Exclude payment
    optionalStepsFailed: Object.keys(ignoredFailures).length,
  };
});

Use Cases

Track which optional services failed while still completing the main workflow:
const flow = await flowProducer.add({
  name: 'user-registration',
  queueName: 'registration',
  data: { email: '[email protected]' },
  children: [
    {
      name: 'create-account',
      queueName: 'accounts',
      data: {},
      // Required
    },
    {
      name: 'subscribe-newsletter',
      queueName: 'newsletter',
      data: {},
      opts: { ignoreDependencyOnFailure: true },
    },
    {
      name: 'setup-trial',
      queueName: 'trials',
      data: {},
      opts: { ignoreDependencyOnFailure: true },
    },
    {
      name: 'send-welcome-email',
      queueName: 'emails',
      data: {},
      opts: { ignoreDependencyOnFailure: true },
    },
  ],
});

const registrationWorker = new Worker('registration', async job => {
  const ignoredFailures = await job.getIgnoredChildrenFailures();
  
  // Complete registration even if optional steps failed
  const result = {
    email: job.data.email,
    accountCreated: true,
    optionalSteps: {
      newsletter: !('newsletter' in ignoredFailures),
      trial: !('trial' in ignoredFailures),
      welcomeEmail: !('welcomeEmail' in ignoredFailures),
    },
  };
  
  // Alert if critical optional steps failed
  if ('welcomeEmail' in ignoredFailures) {
    await alertOps('Welcome email failed', ignoredFailures.welcomeEmail);
  }
  
  return result;
});
Process data with optional enrichment steps while tracking which enrichments failed:
const flow = await flowProducer.add({
  name: 'process-lead',
  queueName: 'leads',
  data: { leadId: 'LEAD-456' },
  children: [
    {
      name: 'validate-lead',
      queueName: 'validation',
      data: {},
      // Required
    },
    {
      name: 'enrich-company-data',
      queueName: 'enrichment',
      data: { source: 'clearbit' },
      opts: { ignoreDependencyOnFailure: true },
    },
    {
      name: 'enrich-social-data',
      queueName: 'enrichment',
      data: { source: 'linkedin' },
      opts: { ignoreDependencyOnFailure: true },
    },
    {
      name: 'score-lead',
      queueName: 'scoring',
      data: {},
      opts: { ignoreDependencyOnFailure: true },
    },
  ],
});

const leadWorker = new Worker('leads', async job => {
  const childrenValues = await job.getChildrenValues();
  const ignoredFailures = await job.getIgnoredChildrenFailures();
  
  // Build lead profile with available data
  const leadProfile = {
    leadId: job.data.leadId,
    validated: true,
    companyData: childrenValues['enrich-company-data'] || null,
    socialData: childrenValues['enrich-social-data'] || null,
    score: childrenValues['score-lead'] || 'unscored',
  };
  
  // Track enrichment success rate
  await trackMetrics({
    totalEnrichments: 3,
    successfulEnrichments: 3 - Object.keys(ignoredFailures).length,
    failedEnrichments: ignoredFailures,
  });
  
  return leadProfile;
});
Send notifications across multiple channels and track which channels failed:
const flow = await flowProducer.add({
  name: 'send-alert',
  queueName: 'alerts',
  data: { alertId: 'ALERT-789', message: 'System warning' },
  children: [
    {
      name: 'send-email',
      queueName: 'notifications',
      data: { channel: 'email' },
      opts: { ignoreDependencyOnFailure: true },
    },
    {
      name: 'send-sms',
      queueName: 'notifications',
      data: { channel: 'sms' },
      opts: { ignoreDependencyOnFailure: true },
    },
    {
      name: 'send-push',
      queueName: 'notifications',
      data: { channel: 'push' },
      opts: { ignoreDependencyOnFailure: true },
    },
    {
      name: 'send-slack',
      queueName: 'notifications',
      data: { channel: 'slack' },
      opts: { ignoreDependencyOnFailure: true },
    },
  ],
});

const alertWorker = new Worker('alerts', async job => {
  const childrenValues = await job.getChildrenValues();
  const ignoredFailures = await job.getIgnoredChildrenFailures();
  
  const successfulChannels = Object.keys(childrenValues);
  const failedChannels = Object.keys(ignoredFailures);
  
  if (successfulChannels.length === 0) {
    // All channels failed - escalate
    await escalateAlert(job.data.alertId, ignoredFailures);
    throw new Error('All notification channels failed');
  }
  
  return {
    alertId: job.data.alertId,
    status: 'sent',
    successfulChannels,
    failedChannels,
    deliveryRate: `${successfulChannels.length}/${successfulChannels.length + failedChannels.length}`,
  };
});

Difference from removeDependencyOnFailure

Understanding when to use ignoreDependencyOnFailure vs removeDependencyOnFailure:

ignoreDependencyOnFailure

Use when:
  • You need to track which children failed
  • You want to log or monitor failures
  • You need to make decisions based on failure information
  • You want better observability
Behavior:
  • Keeps failed child in dependencies as “ignored”
  • Can retrieve failure info with getIgnoredChildrenFailures()
  • Better for debugging and monitoring

removeDependencyOnFailure

Use when:
  • You don’t need to track failures
  • You want to completely forget about failed children
  • You want simpler cleanup
  • Failure information is not important
Behavior:
  • Completely removes failed child from dependencies
  • Cannot retrieve failure information
  • Cleaner but less observable

Monitoring Ignored Dependencies

Track ignored dependencies for better observability:
import { QueueEvents, Queue } from 'bullmq';

const queue = new Queue('topQueueName', { connection });
const queueEvents = new QueueEvents('topQueueName', { connection });

queueEvents.on('completed', async ({ jobId }) => {
  const job = await queue.getJob(jobId);
  
  // Check if job is a parent
  const { ignored } = await job.getDependenciesCount({ ignored: true });
  
  if (ignored > 0) {
    const ignoredFailures = await job.getIgnoredChildrenFailures();
    console.log(`Job ${jobId} completed with ${ignored} ignored failures:`);
    console.log(JSON.stringify(ignoredFailures, null, 2));
    
    // Send to monitoring system
    await reportToMonitoring({
      jobId,
      ignoredCount: ignored,
      failures: ignoredFailures,
    });
  }
});

Advanced Pattern: Retry Failed Optional Steps

You can implement a pattern where the parent retries failed optional steps:
const parentWorker = new Worker('topQueueName', async job => {
  const ignoredFailures = await job.getIgnoredChildrenFailures();
  
  if (Object.keys(ignoredFailures).length > 0) {
    // Try to retry failed optional steps
    for (const [childKey, error] of Object.entries(ignoredFailures)) {
      const [prefix, queueName, jobId] = childKey.split(':');
      
      // Add a retry job
      await retryQueue.add('retry-optional', {
        originalJobId: jobId,
        queueName,
        error,
        parentJobId: job.id,
      });
    }
    
    console.log(`Queued ${Object.keys(ignoredFailures).length} optional steps for retry`);
  }
  
  // Process with successful children
  const childrenValues = await job.getChildrenValues();
  return processResults(childrenValues);
});

Getting Dependency Counts

You can check how many children were ignored:
const { ignored, processed, failed, unprocessed } = 
  await job.getDependenciesCount();

console.log(`
  Processed: ${processed}
  Ignored: ${ignored}
  Failed: ${failed}
  Unprocessed: ${unprocessed}
`);

// Or get only ignored count
const { ignored } = await job.getDependenciesCount({ ignored: true });

Best Practices

Always Check Ignored Failures

Use getIgnoredChildrenFailures() in parent processor to handle failure scenarios

Log for Observability

Log ignored failures to monitoring systems for tracking and alerting

Use for Non-Critical Features

Apply to features that enhance but aren’t critical to the workflow

Consider Retry Logic

Implement retry mechanisms for failed optional steps when appropriate
Use ignoreDependencyOnFailure instead of removeDependencyOnFailure when you need better observability. The ability to retrieve failure information is valuable for monitoring, debugging, and making informed decisions in the parent processor.
Even though failures are “ignored”, they still represent errors in your system. Always log and monitor ignored failures to ensure optional features are working as expected.

API Reference

Build docs developers (and LLMs) love