In some situations, you may have a parent job and need to remove the relationship when one of its children fails. The removeDependencyOnFailure option allows the parent to complete without waiting for failed children.
How It Works
The removeDependencyOnFailure option makes sure that when a job fails, the dependency is removed from the parent, so the parent will complete without waiting for the failed children.
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: { removeDependencyOnFailure: 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' ,
},
],
});
Behavior
When a child job with removeDependencyOnFailure: true fails:
The dependency relationship is removed from the parent
The failed child is no longer tracked in the parent’s dependencies
The parent can complete without waiting for this child
If this was the last pending child, the parent moves to waiting state
Child job fails
A child with removeDependencyOnFailure: true encounters an error and moves to failed state.
Dependency removed
BullMQ removes this child from the parent’s dependency list.
Check remaining dependencies
If there are still other pending children, the parent continues waiting.
Parent becomes active
Once all other children complete, the parent moves to waiting state and can be processed.
Example with Multiple Children
const flow = await flowProducer . add ({
name: 'data-processing' ,
queueName: 'processing' ,
data: { batchId: '123' },
children: [
{
name: 'required-validation' ,
queueName: 'validation' ,
data: { type: 'required' },
// No removeDependencyOnFailure - this MUST succeed
},
{
name: 'optional-enrichment' ,
queueName: 'enrichment' ,
data: { type: 'optional' },
opts: { removeDependencyOnFailure: true },
// Can fail without blocking parent
},
{
name: 'best-effort-analysis' ,
queueName: 'analysis' ,
data: { type: 'best-effort' },
opts: { removeDependencyOnFailure: true },
// Can fail without blocking parent
},
],
});
In this example:
If optional-enrichment or best-effort-analysis fails, they are removed from dependencies
The parent will process once required-validation completes
If required-validation fails, the parent will not process (default behavior)
Use Cases
Process data with optional enrichment steps that can fail without affecting the core workflow: const flow = await flowProducer . add ({
name: 'process-user-data' ,
queueName: 'user-processing' ,
data: { userId: '12345' },
children: [
{
name: 'validate-user' ,
queueName: 'validation' ,
data: {},
// Required - must succeed
},
{
name: 'enrich-with-social' ,
queueName: 'enrichment' ,
data: { source: 'social-media' },
opts: { removeDependencyOnFailure: true },
// Optional - can fail
},
{
name: 'enrich-with-demographics' ,
queueName: 'enrichment' ,
data: { source: 'demographics-api' },
opts: { removeDependencyOnFailure: true },
// Optional - can fail
},
],
});
const userWorker = new Worker ( 'user-processing' , async job => {
// Process user data with whatever enrichment succeeded
const childrenValues = await job . getChildrenValues ();
return {
userId: job . data . userId ,
validated: true ,
enrichments: childrenValues ,
};
});
Best-Effort Notifications
Send notifications that shouldn’t block the main workflow: const orderFlow = await flowProducer . add ({
name: 'process-order' ,
queueName: 'orders' ,
data: { orderId: 'ORD-123' },
children: [
{
name: 'charge-payment' ,
queueName: 'payments' ,
data: {},
// Required - must succeed
},
{
name: 'update-inventory' ,
queueName: 'inventory' ,
data: {},
// Required - must succeed
},
{
name: 'send-sms-notification' ,
queueName: 'notifications' ,
data: { type: 'sms' },
opts: { removeDependencyOnFailure: true },
// Optional - SMS failure shouldn't block order
},
{
name: 'send-email-receipt' ,
queueName: 'notifications' ,
data: { type: 'email' },
opts: { removeDependencyOnFailure: true },
// Optional - email failure shouldn't block order
},
],
});
Track analytics without blocking the main business logic: const signupFlow = await flowProducer . add ({
name: 'complete-signup' ,
queueName: 'signups' ,
data: { email: '[email protected] ' },
children: [
{
name: 'create-account' ,
queueName: 'accounts' ,
data: {},
// Required
},
{
name: 'send-welcome-email' ,
queueName: 'emails' ,
data: {},
// Required
},
{
name: 'track-analytics' ,
queueName: 'analytics' ,
data: { event: 'signup' },
opts: { removeDependencyOnFailure: true },
// Optional - analytics failure shouldn't block signup
},
{
name: 'update-crm' ,
queueName: 'crm' ,
data: {},
opts: { removeDependencyOnFailure: true },
// Optional - CRM failure shouldn't block signup
},
],
});
Difference from ignoreDependencyOnFailure
removeDependencyOnFailure is similar to ignoreDependencyOnFailure, but with key differences:
removeDependencyOnFailure
Completely removes the child from dependencies
Failed child is not tracked
Cannot retrieve failed child information later
ignoreDependencyOnFailure
Keeps the child in dependencies
Failed child is tracked as “ignored”
Can retrieve failed child information using getIgnoredChildrenFailures()
// With removeDependencyOnFailure
const flow1 = await flowProducer . add ({
name: 'parent' ,
queueName: 'queue' ,
children: [
{
name: 'child' ,
queueName: 'queue' ,
opts: { removeDependencyOnFailure: true },
},
],
});
// Parent processor
const worker1 = new Worker ( 'queue' , async job => {
if ( job . name === 'parent' ) {
// Cannot access removed child information
const childrenValues = await job . getChildrenValues ();
// childrenValues will not include the failed child
}
});
// With ignoreDependencyOnFailure
const flow2 = await flowProducer . add ({
name: 'parent' ,
queueName: 'queue' ,
children: [
{
name: 'child' ,
queueName: 'queue' ,
opts: { ignoreDependencyOnFailure: true },
},
],
});
// Parent processor
const worker2 = new Worker ( 'queue' , async job => {
if ( job . name === 'parent' ) {
// Can access ignored child information
const ignoredFailures = await job . getIgnoredChildrenFailures ();
// ignoredFailures will include the failed child
}
});
Monitoring Removed Dependencies
You can track when dependencies are removed:
import { QueueEvents } from 'bullmq' ;
const queueEvents = new QueueEvents ( 'topQueueName' , { connection });
queueEvents . on ( 'waiting' , async ({ jobId }) => {
const job = await queue . getJob ( jobId );
if ( job . parentKey ) {
console . log ( `Child ${ jobId } completed, parent can now process` );
}
});
queueEvents . on ( 'failed' , async ({ jobId , failedReason }) => {
const job = await queue . getJob ( jobId );
const opts = job . opts ;
if ( opts . removeDependencyOnFailure ) {
console . log ( `Child ${ jobId } failed but was removed from parent dependencies` );
}
});
Parent Processor Example
Handle the case where some children were removed:
import { Worker } from 'bullmq' ;
const parentWorker = new Worker ( 'topQueueName' , async job => {
const childrenValues = await job . getChildrenValues ();
// Get dependency counts to see what happened
const { processed , failed } = await job . getDependenciesCount ();
console . log ( `Processed: ${ processed } , Failed (removed): ${ failed } ` );
// Process with whatever children succeeded
const results = Object . values ( childrenValues );
if ( results . length === 0 ) {
return { status: 'completed' , note: 'All optional children failed' };
}
return {
status: 'completed' ,
processedCount: results . length ,
data: processResults ( results ),
};
});
Best Practices
Use for Optional Features Apply to children that provide optional enhancements or features
Not for Critical Paths Never use for children that are critical to the workflow
Log Removed Dependencies Track when dependencies are removed for monitoring and debugging
Handle Missing Data Parent should handle cases where optional children didn’t provide data
Use removeDependencyOnFailure carefully. The parent job will have no record of the failed child, making debugging more difficult. Consider using ignoreDependencyOnFailure if you need to track failed optional children.
For better observability, use ignoreDependencyOnFailure instead of removeDependencyOnFailure when you want to keep track of which optional children failed.
API Reference